diff options
21 files changed, 380 insertions, 167 deletions
diff --git a/cds-ui/designer-client/src/app/common/constants/app-constants.ts b/cds-ui/designer-client/src/app/common/constants/app-constants.ts index eeff3d41f..14cb001c8 100644 --- a/cds-ui/designer-client/src/app/common/constants/app-constants.ts +++ b/cds-ui/designer-client/src/app/common/constants/app-constants.ts @@ -118,3 +118,6 @@ export const ControllerCatalogURLs = { getDefinition: '/controllercatalog/model-type/by-definition', getDerivedFrom: '/controllercatalog/model-type/by-derivedfrom' }; + + +export const ActionElementTypeName = 'app.ActionElement'; diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/packages/designer/designer.component.html b/cds-ui/designer-client/src/app/modules/feature-modules/packages/designer/designer.component.html index 8ec735aec..311ce7ad9 100644 --- a/cds-ui/designer-client/src/app/modules/feature-modules/packages/designer/designer.component.html +++ b/cds-ui/designer-client/src/app/modules/feature-modules/packages/designer/designer.component.html @@ -61,20 +61,20 @@ [mode]="'push'" #sidebarLeft> <div class="row"> - <div class="col-12 p-0"> + <!-- <div class="col-12 p-0"> <form> <input type="text" class="form-control input-search-controller" placeholder="Search actions and functions"> </form> - </div> + </div> --> <h1 class="col-12">Actions</h1> <div class="col-12 text-center p-0"> <div class="btn-group actionBtns" role="group"> <button (click)="insertCustomActionIntoBoard()" type="button" class="btn">Insert Custom</button> - <button type="button" class="btn">Import Action</button> + <!-- <button type="button" class="btn">Import Action</button> --> </div> </div> - <div class="col-12 actionsList"> + <!-- <div class="col-12 actionsList"> <b>Select from other packages:</b> <div class="actions-scroll"> <div class="custom-control custom-checkbox"> @@ -106,7 +106,7 @@ <button type="button" class="btn btn-secondary mr-3">Insert</button> <button type="button" class="btn btn-secondary">Cancel</button> </div> - </div> + </div> --> <h1 class="col-12">Functions</h1> <div id="palette-paper" class="col-12 componentsList"> diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/packages/designer/designer.component.ts b/cds-ui/designer-client/src/app/modules/feature-modules/packages/designer/designer.component.ts index b19f5699b..130e0ae19 100644 --- a/cds-ui/designer-client/src/app/modules/feature-modules/packages/designer/designer.component.ts +++ b/cds-ui/designer-client/src/app/modules/feature-modules/packages/designer/designer.component.ts @@ -3,6 +3,8 @@ import * as joint from 'jointjs'; import './jointjs/elements/palette.function.element'; import './jointjs/elements/action.element'; import './jointjs/elements/board.function.element'; +import { DesignerStore } from './designer.store'; +import { ActionElementTypeName } from 'src/app/common/constants/app-constants'; @@ -26,7 +28,7 @@ export class DesignerComponent implements OnInit { paletteGraph: joint.dia.Graph; palettePaper: joint.dia.Paper; - constructor() { + constructor(private designerStore: DesignerStore) { this.controllerSideBar = true; this.attributesSideBar = false; } @@ -54,22 +56,37 @@ export class DesignerComponent implements OnInit { this.initializeBoard(); this.initializePalette(); // this.createEditBarOverThePaper(); + + //functions list is contants for now const list = [ { modelName: 'component-netconf-executor'}, { modelName: 'component-remote-ansible-executor' }, { modelName: 'dg-generic' }, { modelName: 'component-resource-resolution' }]; + const cells = this.buildPaletteGraphFromList(list); + this.paletteGraph.resetCells(cells); - const cells = this.buildPaletteGraphFromList(list); - this.paletteGraph.resetCells(cells); - - let idx = 0; - cells.forEach(cell => { - console.log(cell); - cell.translate(5, (cell.attributes.size.height + 5) * idx++); + let idx = 0; + cells.forEach(cell => { + console.log(cell); + cell.translate(5, (cell.attributes.size.height + 5) * idx++); - }); - this.stencilPaperEventListeners(); + }); + this.stencilPaperEventListeners(); + /** + * the code to retrieve from server is commented + */ + // this.designerStore.state$.subscribe(state => { + // console.log(state); + // if (state.functions) { + // console.log('functions-->' , state.functions); + // // this.viewedFunctions = state.functions; + // const list = state.functions; + // } + // }); + //action triggering + // this.designerStore.getFuntions(); + } initializePalette() { @@ -127,9 +144,6 @@ export class DesignerComponent implements OnInit { var parentBbox = parent.getBBox(); var cellBbox = cell.getBBox(); - - console.log("parent ", parentBbox); - console.log("cell ", cellBbox); if (parentBbox.containsPoint(cellBbox.origin()) && parentBbox.containsPoint(cellBbox.topRight()) && parentBbox.containsPoint(cellBbox.corner()) && @@ -149,8 +163,12 @@ export class DesignerComponent implements OnInit { insertCustomActionIntoBoard() { this.actionIdCounter++; - const element = this.createCustomAction("action_"+ this.actionIdCounter, 'Action' + this.actionIdCounter); + const actionId = "action_" + this.actionIdCounter; + const actionName = 'Action' + this.actionIdCounter; + const element = this.createCustomAction(actionId , actionName); this.boardGraph.addCell(element); + console.log('saving action to store action workflow....'); + this.designerStore.addDeclarativeWorkFlow(actionName); } createCustomAction(id: string, label: string) { @@ -192,7 +210,6 @@ export class DesignerComponent implements OnInit { stencilPaperEventListeners() { this.palettePaper.on('cell:pointerdown', (draggedCell, pointerDownEvent, x, y) => { - console.log('pointerdown 2'); $('body').append(` <div id="flyPaper" @@ -232,38 +249,30 @@ export class DesignerComponent implements OnInit { if (mouseupX > target.left && mouseupX < target.left + this.boardPaper.$el.width() && mouseupY > target.top && y < target.top + this.boardPaper.$el.height()) { - // const clonedShape = flyShape.clone(); - const type = flyShape.attributes.attrs.type; - console.log(type); - - //create board function element of the same type of palette function - //board function element is different in design from the palette function element - this.fuctionIdCounter++; - console.log(this.fuctionIdCounter); - const functionElementForBoard = - this.createFuctionElementForBoard("fucntion_" + this.fuctionIdCounter, 'execute', type); - - functionElementForBoard.position(mouseupX - target.left - offset.x, mouseupY - target.top - offset.y); - this.boardGraph.addCell(functionElementForBoard); - const cellViewsBelow = - this.boardPaper.findViewsFromPoint(functionElementForBoard.getBBox().center()); - console.log(cellViewsBelow); - if (cellViewsBelow.length) { - let cellViewBelow; - cellViewsBelow.forEach( cellItem => { - if (cellItem.model.id !== functionElementForBoard.id) { - cellViewBelow = cellItem; - } - }); + const functionType = flyShape.attributes.attrs.type; + console.log(functionType); + const functionElementForBoard = this.dropFunctionOverAction(functionType, mouseupX, target, offset, mouseupY); + + let parentCell = this.getParent(functionElementForBoard); + + console.log("parentCell -->", parentCell); + + if (parentCell && + parentCell.model.attributes.type === ActionElementTypeName){ + + const actionName = parentCell.model.attributes.attrs['#label'].text; + this.designerStore.addStepToDeclarativeWorkFlow(actionName, functionType); + this.designerStore.addNodeTemplate(functionType); // Prevent recursive embedding. - if (cellViewBelow && cellViewBelow.model.get('parent') !== functionElementForBoard.id) { - console.log(cellViewBelow); - cellViewBelow.model.embed(functionElementForBoard); + if (parentCell && + parentCell.model.get('parent') !== functionElementForBoard.id) { + parentCell.model.embed(functionElementForBoard); } - console.log(this.boardGraph); + }else{ + console.log('function dropped outside action, rolling back...'); + functionElementForBoard.remove(); } - } $('body').off('mousemove.fly').off('mouseup.fly'); // flyShape.remove(); @@ -272,6 +281,32 @@ export class DesignerComponent implements OnInit { }); } + private getParent(functionElementForBoard: joint.shapes.board.FunctionElement) { + const cellViewsBelow = this.boardPaper.findViewsFromPoint(functionElementForBoard.getBBox().center()); + let cellViewBelow; + if (cellViewsBelow.length) { + cellViewsBelow.forEach(cellItem => { + if (cellItem.model.id !== functionElementForBoard.id) { + cellViewBelow = cellItem; + } + }); + } + return cellViewBelow; + } + + /** + * trigger actions related to Function dropped over the board: + * - create board function element of the same type of palette function + * as board function element is different from the palette function element + * - save function to parent action in store + */ + private dropFunctionOverAction(functionType: any, mouseupX: number, target: JQuery.Coordinates, offset: { x: number; y: number; }, mouseupY: number) { + this.fuctionIdCounter++; + const functionElementForBoard = this.createFuctionElementForBoard("fucntion_" + this.fuctionIdCounter, 'execute', functionType); + functionElementForBoard.position(mouseupX - target.left - offset.x, mouseupY - target.top - offset.y); + this.boardGraph.addCell(functionElementForBoard); + return functionElementForBoard; + } /** * this is a way to add the button like zoom in , zoom out , and source over jointjs paper * may be used if no other way is found diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/packages/designer/designer.service.ts b/cds-ui/designer-client/src/app/modules/feature-modules/packages/designer/designer.service.ts index c4564254f..aa3a6a668 100644 --- a/cds-ui/designer-client/src/app/modules/feature-modules/packages/designer/designer.service.ts +++ b/cds-ui/designer-client/src/app/modules/feature-modules/packages/designer/designer.service.ts @@ -23,7 +23,7 @@ import {Injectable} from '@angular/core'; import {Observable} from 'rxjs'; import {ApiService} from '../../../../common/core/services/api.typed.service'; import {ResourceDictionaryURLs} from '../../../../common/constants/app-constants'; -import {ModelType} from '../model/ModelType.model'; +import {ModelType} from './model/ModelType.model'; @Injectable({ diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/packages/designer/designer.store.ts b/cds-ui/designer-client/src/app/modules/feature-modules/packages/designer/designer.store.ts index be98eec88..f2972d03b 100644 --- a/cds-ui/designer-client/src/app/modules/feature-modules/packages/designer/designer.store.ts +++ b/cds-ui/designer-client/src/app/modules/feature-modules/packages/designer/designer.store.ts @@ -22,8 +22,10 @@ limitations under the License. import {Injectable} from '@angular/core'; import {Store} from '../../../../common/core/stores/Store'; import {DesignerService} from './designer.service'; -import {ModelType} from '../model/ModelType.model'; -import {DesignerDashboardState} from '../model/designer-dashboard.state'; +import {ModelType} from './model/ModelType.model'; +import {DesignerDashboardState} from './model/designer.dashboard.state'; +import { DeclarativeWorkflow } from './model/designer.workflow'; +import { NodeTemplate } from './model/desinger.nodeTemplate.model'; @Injectable({ @@ -35,15 +37,60 @@ export class DesignerStore extends Store<DesignerDashboardState> { super(new DesignerDashboardState()); } - public getFuntions() { + public retrieveFuntions() { const modelDefinitionType = 'node_type'; this.designerService.getFunctions(modelDefinitionType).subscribe( - (modelType: ModelType[]) => { - console.log(modelType); + (modelTypeList: ModelType[]) => { + console.log(modelTypeList); this.setState({ ...this.state, - functions: modelType, + serverFunctions: modelTypeList, }); }); } + + /** + * adds empty workflow with name only. + * called when blank action is added to the board + * declarative workflow just contain the steps but its order is determind by dg-graph + */ + addDeclarativeWorkFlow(workflowName: string) { + this.setState({ + ...this.state, + template: { + ...this.state.template, + workflows: + this.state.template.workflows.set(workflowName, new DeclarativeWorkflow()) + } + }); + } + + addStepToDeclarativeWorkFlow(workflowName: string, stepType: string) { + const currentWorkflow: DeclarativeWorkflow = this.state.template.workflows.get(workflowName); + currentWorkflow.steps = { + target: stepType, + description: '' + }; + const allNewWorkflowsMap = + this.state.template.workflows.set(workflowName, currentWorkflow); + this.setState({ + ...this.state, + template: { + ...this.state.template, + workflows: allNewWorkflowsMap + } + }); + } + + + addNodeTemplate(nodeTemplateName: string) { + this.setState({ + ...this.state, + template: { + ...this.state.template, + node_templates: + this.state.template.node_templates.set(nodeTemplateName, new NodeTemplate()) + } + }); + } } diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/packages/designer/functions/functions.component.ts b/cds-ui/designer-client/src/app/modules/feature-modules/packages/designer/functions/functions.component.ts index 5a86150e8..e1e980ed9 100644 --- a/cds-ui/designer-client/src/app/modules/feature-modules/packages/designer/functions/functions.component.ts +++ b/cds-ui/designer-client/src/app/modules/feature-modules/packages/designer/functions/functions.component.ts @@ -1,6 +1,6 @@ import {Component, OnInit} from '@angular/core'; import {DesignerStore} from '../designer.store'; -import {ModelType} from '../../model/ModelType.model'; +import {ModelType} from '../model/ModelType.model'; @Component({ @@ -15,14 +15,14 @@ export class FunctionsComponent implements OnInit { this.designerStore.state$.subscribe(state => { console.log(state); - if (state.functions) { - this.viewedFunctions = state.functions; + if (state.serverFunctions) { + this.viewedFunctions = state.serverFunctions; } }); } ngOnInit() { - this.designerStore.getFuntions(); + this.designerStore.retrieveFuntions(); } } diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/packages/designer/jointjs/elements/action.element.ts b/cds-ui/designer-client/src/app/modules/feature-modules/packages/designer/jointjs/elements/action.element.ts index 212905814..7960e83d1 100644 --- a/cds-ui/designer-client/src/app/modules/feature-modules/packages/designer/jointjs/elements/action.element.ts +++ b/cds-ui/designer-client/src/app/modules/feature-modules/packages/designer/jointjs/elements/action.element.ts @@ -1,4 +1,5 @@ import * as joint from 'jointjs'; +import { ActionElementTypeName } from 'src/app/common/constants/app-constants'; /** * please refer to documentation in file palette.function.element.ts to get more details * about how to create new element type and define it in typescript @@ -18,7 +19,7 @@ const rectWidth = 616; const rectHeight = 381; // custom element implementation // https://resources.jointjs.com/tutorials/joint/tutorials/custom-elements.html#markup -const ActionElement = joint.shapes.standard.Rectangle.define('app.ActionElement', { +const ActionElement = joint.shapes.standard.Rectangle.define(ActionElementTypeName, { size: {width: rectWidth, height: rectHeight} }, { diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/packages/model/ModelType.model.ts b/cds-ui/designer-client/src/app/modules/feature-modules/packages/designer/model/ModelType.model.ts index c8498fa36..c8498fa36 100644 --- a/cds-ui/designer-client/src/app/modules/feature-modules/packages/model/ModelType.model.ts +++ b/cds-ui/designer-client/src/app/modules/feature-modules/packages/designer/model/ModelType.model.ts diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/packages/model/designer-dashboard.state.ts b/cds-ui/designer-client/src/app/modules/feature-modules/packages/designer/model/designer.dashboard.state.ts index dc65c009f..5ae62d84a 100644 --- a/cds-ui/designer-client/src/app/modules/feature-modules/packages/model/designer-dashboard.state.ts +++ b/cds-ui/designer-client/src/app/modules/feature-modules/packages/designer/model/designer.dashboard.state.ts @@ -20,10 +20,15 @@ limitations under the License. */ import {ModelType} from './ModelType.model'; +import { TopologyTemplate } from './designer.topologyTemplate.model'; export class DesignerDashboardState { - functions: ModelType[]; - actions: string[]; + serverFunctions: ModelType[]; + template: TopologyTemplate; + constructor() { + this.serverFunctions = []; + this.template = new TopologyTemplate(); + } } diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/packages/designer/model/designer.topologyTemplate.model.ts b/cds-ui/designer-client/src/app/modules/feature-modules/packages/designer/model/designer.topologyTemplate.model.ts new file mode 100644 index 000000000..4e73c7986 --- /dev/null +++ b/cds-ui/designer-client/src/app/modules/feature-modules/packages/designer/model/designer.topologyTemplate.model.ts @@ -0,0 +1,13 @@ +import { DeclarativeWorkflow } from './designer.workflow'; +import { NodeTemplate } from './desinger.nodeTemplate.model'; + +export class TopologyTemplate { + + workflows: Map<string, DeclarativeWorkflow>; + 'node_templates': Map<string, NodeTemplate>; + + constructor() { + this.workflows = new Map(); + this.node_templates = new Map(); + } +} diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/packages/designer/model/designer.workflow.ts b/cds-ui/designer-client/src/app/modules/feature-modules/packages/designer/model/designer.workflow.ts new file mode 100644 index 000000000..0687c1f47 --- /dev/null +++ b/cds-ui/designer-client/src/app/modules/feature-modules/packages/designer/model/designer.workflow.ts @@ -0,0 +1,14 @@ +export class Workflow { + inputs: {}; + outputs?: {}; +} + +export class DeclarativeWorkflow implements Workflow { + steps: {}; + inputs: {}; + outputs?: {}; + + constructor() { + this.steps = {}; + } +} diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/packages/designer/model/desinger.nodeTemplate.model.ts b/cds-ui/designer-client/src/app/modules/feature-modules/packages/designer/model/desinger.nodeTemplate.model.ts new file mode 100644 index 000000000..7f8556535 --- /dev/null +++ b/cds-ui/designer-client/src/app/modules/feature-modules/packages/designer/model/desinger.nodeTemplate.model.ts @@ -0,0 +1,10 @@ +export class NodeTemplate { + type: string; + properties?: { + 'dependency-node-template'?: string[] + }; + interfaces?: {}; + artifacts?: {}; + cabapilities?: {}; + requirements?: {}; +} diff --git a/ms/blueprintsprocessor/modules/blueprints/blueprint-core/pom.xml b/ms/blueprintsprocessor/modules/blueprints/blueprint-core/pom.xml index 28060ef44..fb2daab3a 100644 --- a/ms/blueprintsprocessor/modules/blueprints/blueprint-core/pom.xml +++ b/ms/blueprintsprocessor/modules/blueprints/blueprint-core/pom.xml @@ -91,6 +91,10 @@ <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </dependency> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-compress</artifactId> + </dependency> <!--Testing dependencies--> <dependency> <groupId>org.jetbrains.kotlin</groupId> diff --git a/ms/blueprintsprocessor/modules/blueprints/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/utils/BluePrintArchiveUtils.kt b/ms/blueprintsprocessor/modules/blueprints/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/utils/BluePrintArchiveUtils.kt index 9ccf856b5..595dbce6b 100755 --- a/ms/blueprintsprocessor/modules/blueprints/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/utils/BluePrintArchiveUtils.kt +++ b/ms/blueprintsprocessor/modules/blueprints/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/utils/BluePrintArchiveUtils.kt @@ -22,22 +22,39 @@ import com.google.common.base.Predicates import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintProcessorException import org.slf4j.LoggerFactory import java.io.BufferedInputStream +import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream +import java.io.Closeable import java.io.File import java.io.FileOutputStream +import java.io.InputStream +import java.io.InputStreamReader import java.io.IOException import java.io.OutputStream -import java.nio.charset.Charset import java.nio.file.FileVisitResult import java.nio.file.Files import java.nio.file.Path import java.nio.file.SimpleFileVisitor import java.nio.file.attribute.BasicFileAttributes import java.util.function.Predicate +import org.apache.commons.compress.archivers.ArchiveEntry +import org.apache.commons.compress.archivers.ArchiveInputStream +import org.apache.commons.compress.archivers.ArchiveOutputStream +import org.apache.commons.compress.archivers.tar.TarArchiveEntry +import org.apache.commons.compress.archivers.tar.TarArchiveInputStream +import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream +import org.apache.commons.compress.archivers.zip.ZipArchiveEntry +import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream +import org.apache.commons.compress.archivers.zip.ZipFile +import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream +import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream +import java.util.Enumeration import java.util.zip.Deflater -import java.util.zip.ZipEntry -import java.util.zip.ZipFile -import java.util.zip.ZipOutputStream + +enum class ArchiveType { + TarGz, + Zip +} class BluePrintArchiveUtils { @@ -51,7 +68,7 @@ class BluePrintArchiveUtils { * @param destination the output filename * @return True if OK */ - fun compress(source: File, destination: File): Boolean { + fun compress(source: File, destination: File, archiveType: ArchiveType = ArchiveType.Zip): Boolean { try { if (!destination.parentFile.exists()) { destination.parentFile.mkdirs() @@ -59,7 +76,7 @@ class BluePrintArchiveUtils { destination.createNewFile() val ignoreZipFiles = Predicate<Path> { path -> !path.endsWith(".zip") && !path.endsWith(".ZIP") } FileOutputStream(destination).use { out -> - compressFolder(source.toPath(), out, pathFilter = ignoreZipFiles) + compressFolder(source.toPath(), out, archiveType, pathFilter = ignoreZipFiles) } } catch (e: Exception) { log.error("Fail to compress folder($source) to path(${destination.path})", e) @@ -71,8 +88,12 @@ class BluePrintArchiveUtils { /** * In-memory compress an entire folder. */ - fun compressToBytes(baseDir: Path, compressionLevel: Int = Deflater.NO_COMPRESSION): ByteArray { - return compressFolder(baseDir, ByteArrayOutputStream(), compressionLevel = compressionLevel) + fun compressToBytes( + baseDir: Path, + archiveType: ArchiveType = ArchiveType.Zip, + compressionLevel: Int = Deflater.NO_COMPRESSION + ): ByteArray { + return compressFolder(baseDir, ByteArrayOutputStream(), archiveType, compressionLevel = compressionLevel) .toByteArray() } @@ -89,38 +110,51 @@ class BluePrintArchiveUtils { private fun <T> compressFolder( baseDir: Path, output: T, + archiveType: ArchiveType, pathFilter: Predicate<Path> = Predicates.alwaysTrue(), compressionLevel: Int = Deflater.DEFAULT_COMPRESSION, fixedModificationTime: Long? = null ): T where T : OutputStream { - ZipOutputStream(output) - .apply { setLevel(compressionLevel) } - .use { zos -> + val stream: ArchiveOutputStream = if (archiveType == ArchiveType.Zip) + ZipArchiveOutputStream(output).apply { setLevel(compressionLevel) } + else + TarArchiveOutputStream(GzipCompressorOutputStream(output)) + stream + .use { aos -> Files.walkFileTree(baseDir, object : SimpleFileVisitor<Path>() { @Throws(IOException::class) override fun visitFile(file: Path, attrs: BasicFileAttributes): FileVisitResult { if (pathFilter.test(file)) { - val zipEntry = ZipEntry(baseDir.relativize(file).toString()) - fixedModificationTime?.let { - zipEntry.time = it + var archiveEntry: ArchiveEntry = aos.createArchiveEntry(file.toFile(), + baseDir.relativize(file).toString()) + if (archiveType == ArchiveType.Zip) { + val entry = archiveEntry as ZipArchiveEntry + fixedModificationTime?.let { + entry.time = it + } + entry.time = 0 } - zipEntry.time = 0 - zos.putNextEntry(zipEntry) - Files.copy(file, zos) - zos.closeEntry() + aos.putArchiveEntry(archiveEntry) + Files.copy(file, aos) + aos.closeArchiveEntry() } return FileVisitResult.CONTINUE } @Throws(IOException::class) override fun preVisitDirectory(dir: Path, attrs: BasicFileAttributes): FileVisitResult { - val zipEntry = ZipEntry(baseDir.relativize(dir).toString() + "/") - fixedModificationTime?.let { - zipEntry.time = it - } - zos.putNextEntry(zipEntry) - zos.closeEntry() + var archiveEntry: ArchiveEntry? + if (archiveType == ArchiveType.Zip) { + val entry = ZipArchiveEntry(baseDir.relativize(dir).toString() + "/") + fixedModificationTime?.let { + entry.time = it + } + archiveEntry = entry + } else + archiveEntry = TarArchiveEntry(baseDir.relativize(dir).toString() + "/") + aos.putArchiveEntry(archiveEntry) + aos.closeArchiveEntry() return FileVisitResult.CONTINUE } }) @@ -128,31 +162,111 @@ class BluePrintArchiveUtils { return output } - fun deCompress(zipFile: File, targetPath: String): File { - val zip = ZipFile(zipFile, Charset.defaultCharset()) - val enumeration = zip.entries() - while (enumeration.hasMoreElements()) { - val entry = enumeration.nextElement() - val destFilePath = File(targetPath, entry.name) - destFilePath.parentFile.mkdirs() + private fun getDefaultEncoding(): String? { + val bytes = byteArrayOf('D'.toByte()) + val inputStream: InputStream = ByteArrayInputStream(bytes) + val reader = InputStreamReader(inputStream) + return reader.encoding + } - if (entry.isDirectory) - continue + fun deCompress(archiveFile: File, targetPath: String, archiveType: ArchiveType = ArchiveType.Zip): File { + var enumeration: ArchiveEnumerator? = null + if (archiveType == ArchiveType.Zip) { + val zipArchive = ZipFile(archiveFile, getDefaultEncoding()) + enumeration = ArchiveEnumerator(zipArchive) + } else { // Tar Gz + var tarGzArchiveIs: InputStream = BufferedInputStream(archiveFile.inputStream()) + tarGzArchiveIs = GzipCompressorInputStream(tarGzArchiveIs) + val tarGzArchive: ArchiveInputStream = TarArchiveInputStream(tarGzArchiveIs) + enumeration = ArchiveEnumerator(tarGzArchive) + } + + enumeration.use { + while (enumeration!!.hasMoreElements()) { + val entry: ArchiveEntry? = enumeration.nextElement() + val destFilePath = File(targetPath, entry!!.name) + destFilePath.parentFile.mkdirs() - val bufferedIs = BufferedInputStream(zip.getInputStream(entry)) - bufferedIs.use { + if (entry!!.isDirectory) + continue + + val bufferedIs = BufferedInputStream(enumeration.getInputStream(entry)) destFilePath.outputStream().buffered(1024).use { bos -> bufferedIs.copyTo(bos) } + + if (!enumeration.getHasSharedEntryInputStream()) + bufferedIs.close() } } val destinationDir = File(targetPath) check(destinationDir.isDirectory && destinationDir.exists()) { - throw BluePrintProcessorException("failed to decompress blueprint(${zipFile.absolutePath}) to ($targetPath) ") + throw BluePrintProcessorException("failed to decompress blueprint(${archiveFile.absolutePath}) to ($targetPath) ") } return destinationDir } } + + class ArchiveEnumerator : Enumeration<ArchiveEntry>, Closeable { + private val zipArchive: ZipFile? + private val zipEnumeration: Enumeration<ZipArchiveEntry>? + private val archiveStream: ArchiveInputStream? + private var nextEntry: ArchiveEntry? = null + private val hasSharedEntryInputStream: Boolean + + constructor(zipFile: ZipFile) { + zipArchive = zipFile + zipEnumeration = zipFile.entries + archiveStream = null + hasSharedEntryInputStream = false + } + + constructor(archiveStream: ArchiveInputStream) { + this.archiveStream = archiveStream + zipArchive = null + zipEnumeration = null + hasSharedEntryInputStream = true + } + + fun getHasSharedEntryInputStream(): Boolean { + return hasSharedEntryInputStream + } + + fun getInputStream(entry: ArchiveEntry): InputStream? { + return if (zipArchive != null) + zipArchive?.getInputStream(entry as ZipArchiveEntry?) + else + archiveStream + } + + override fun hasMoreElements(): Boolean { + if (zipEnumeration != null) + return zipEnumeration?.hasMoreElements() + else if (archiveStream != null) { + nextEntry = archiveStream.nextEntry + if (nextEntry != null && !archiveStream.canReadEntryData(nextEntry)) + return hasMoreElements() + return nextEntry != null + } + return false + } + + override fun nextElement(): ArchiveEntry? { + if (zipEnumeration != null) + nextEntry = zipEnumeration.nextElement() + else if (archiveStream != null) { + if (nextEntry == null) + nextEntry = archiveStream.nextEntry + } + return nextEntry + } + + override fun close() { + if (zipArchive != null) + zipArchive.close() + else archiveStream?.close() + } + } } diff --git a/ms/blueprintsprocessor/modules/commons/processor-core/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/core/cluster/HazlecastClusterServiceTest.kt b/ms/blueprintsprocessor/modules/commons/processor-core/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/core/cluster/HazlecastClusterServiceTest.kt index b298eacae..80cf41558 100644 --- a/ms/blueprintsprocessor/modules/commons/processor-core/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/core/cluster/HazlecastClusterServiceTest.kt +++ b/ms/blueprintsprocessor/modules/commons/processor-core/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/core/cluster/HazlecastClusterServiceTest.kt @@ -113,7 +113,7 @@ class HazlecastClusterServiceTest { } else { ClusterInfo( id = "test-cluster", nodeId = nodeId, joinAsClient = false, - configFile = "./src/test/resources/hazelcast/hazelcast-$port.yaml", + configFile = "./src/test/resources/hazelcast/hazelcast-cluster.yaml", properties = properties ) } @@ -162,7 +162,7 @@ class HazlecastClusterServiceTest { executeLock(bluePrintClusterServices[0], "first", lockName) } val deferred2 = async { - executeLock(bluePrintClusterServices[0], "second", lockName) + executeLock(bluePrintClusterServices[1], "second", lockName) } val deferred3 = async { executeLock(bluePrintClusterServices[2], "third", lockName) diff --git a/ms/blueprintsprocessor/modules/commons/processor-core/src/test/resources/hazelcast/hazelcast-5679.yaml b/ms/blueprintsprocessor/modules/commons/processor-core/src/test/resources/hazelcast/hazelcast-5679.yaml deleted file mode 100644 index e7ac273ed..000000000 --- a/ms/blueprintsprocessor/modules/commons/processor-core/src/test/resources/hazelcast/hazelcast-5679.yaml +++ /dev/null @@ -1,21 +0,0 @@ -hazelcast: - cluster-name: ${CLUSTER_ID} - instance-name: node-5679 - lite-member: - enabled: false - cp-subsystem: - cp-member-count: 3 - group-size: 3 - session-time-to-live-seconds: 60 - session-heartbeat-interval-seconds: 5 - missing-cp-member-auto-removal-seconds: 120 -# network: -# join: -# multicast: -# enabled: false -# kubernetes: -# enabled: true -# namespace: MY-KUBERNETES-NAMESPACE -# service-name: MY-SERVICE-NAME -# service-label-name: MY-SERVICE-LABEL-NAME -# service-label-value: MY-SERVICE-LABEL-VALUE
\ No newline at end of file diff --git a/ms/blueprintsprocessor/modules/commons/processor-core/src/test/resources/hazelcast/hazelcast-5680.yaml b/ms/blueprintsprocessor/modules/commons/processor-core/src/test/resources/hazelcast/hazelcast-5680.yaml deleted file mode 100644 index cb493d169..000000000 --- a/ms/blueprintsprocessor/modules/commons/processor-core/src/test/resources/hazelcast/hazelcast-5680.yaml +++ /dev/null @@ -1,21 +0,0 @@ -hazelcast: - cluster-name: ${CLUSTER_ID} - instance-name: node-5680 - lite-member: - enabled: false - cp-subsystem: - cp-member-count: 3 - group-size: 3 - session-time-to-live-seconds: 60 - session-heartbeat-interval-seconds: 5 - missing-cp-member-auto-removal-seconds: 120 -# network: -# join: -# multicast: -# enabled: false -# kubernetes: -# enabled: true -# namespace: MY-KUBERNETES-NAMESPACE -# service-name: MY-SERVICE-NAME -# service-label-name: MY-SERVICE-LABEL-NAME -# service-label-value: MY-SERVICE-LABEL-VALUE
\ No newline at end of file diff --git a/ms/blueprintsprocessor/modules/commons/processor-core/src/test/resources/hazelcast/hazelcast-5681.yaml b/ms/blueprintsprocessor/modules/commons/processor-core/src/test/resources/hazelcast/hazelcast-5681.yaml deleted file mode 100644 index e60b0c506..000000000 --- a/ms/blueprintsprocessor/modules/commons/processor-core/src/test/resources/hazelcast/hazelcast-5681.yaml +++ /dev/null @@ -1,21 +0,0 @@ -hazelcast: - cluster-name: ${CLUSTER_ID} - instance-name: node-5681 - lite-member: - enabled: false - cp-subsystem: - cp-member-count: 3 - group-size: 3 - session-time-to-live-seconds: 60 - session-heartbeat-interval-seconds: 5 - missing-cp-member-auto-removal-seconds: 120 -# network: -# join: -# multicast: -# enabled: false -# kubernetes: -# enabled: true -# namespace: MY-KUBERNETES-NAMESPACE -# service-name: MY-SERVICE-NAME -# service-label-name: MY-SERVICE-LABEL-NAME -# service-label-value: MY-SERVICE-LABEL-VALUE
\ No newline at end of file diff --git a/ms/blueprintsprocessor/modules/commons/processor-core/src/test/resources/hazelcast/hazelcast-5682.yaml b/ms/blueprintsprocessor/modules/commons/processor-core/src/test/resources/hazelcast/hazelcast-5682.yaml index 3cb10a08b..859ea3328 100644 --- a/ms/blueprintsprocessor/modules/commons/processor-core/src/test/resources/hazelcast/hazelcast-5682.yaml +++ b/ms/blueprintsprocessor/modules/commons/processor-core/src/test/resources/hazelcast/hazelcast-5682.yaml @@ -9,10 +9,11 @@ hazelcast: session-time-to-live-seconds: 60 session-heartbeat-interval-seconds: 5 missing-cp-member-auto-removal-seconds: 120 -# network: -# join: -# multicast: -# enabled: false + network: + join: + multicast: + enabled: true + multicast-group: 224.0.0.1 # kubernetes: # enabled: true # namespace: MY-KUBERNETES-NAMESPACE diff --git a/ms/blueprintsprocessor/modules/commons/processor-core/src/test/resources/hazelcast/hazelcast-cluster.yaml b/ms/blueprintsprocessor/modules/commons/processor-core/src/test/resources/hazelcast/hazelcast-cluster.yaml new file mode 100644 index 000000000..de6047a90 --- /dev/null +++ b/ms/blueprintsprocessor/modules/commons/processor-core/src/test/resources/hazelcast/hazelcast-cluster.yaml @@ -0,0 +1,23 @@ +hazelcast: + lite-member: + enabled: false + cp-subsystem: + cp-member-count: 3 + group-size: 3 + session-time-to-live-seconds: 60 + session-heartbeat-interval-seconds: 5 + missing-cp-member-auto-removal-seconds: 120 + network: + join: + multicast: + enabled: true + # Specify 224.0.0.1 instead of default 224.2.2.3 since there's some issue + # on macOs with docker installed and multicast address different than 224.0.0.1 + # https://stackoverflow.com/questions/46341715/hazelcast-multicast-does-not-work-because-of-vboxnet-which-is-used-by-docker-mac + multicast-group: 224.0.0.1 + # kubernetes: + # enabled: true + # namespace: MY-KUBERNETES-NAMESPACE + # service-name: MY-SERVICE-NAME + # service-label-name: MY-SERVICE-LABEL-NAME + # service-label-value: MY-SERVICE-LABEL-VALUE diff --git a/ms/blueprintsprocessor/parent/pom.xml b/ms/blueprintsprocessor/parent/pom.xml index 8301fbccf..d47889a48 100755 --- a/ms/blueprintsprocessor/parent/pom.xml +++ b/ms/blueprintsprocessor/parent/pom.xml @@ -60,6 +60,7 @@ <json-smart.version>2.3</json-smart.version> <commons-io-version>2.6</commons-io-version> + <commons-compress-version>1.20</commons-compress-version> <commons-collections-version>3.2.2</commons-collections-version> </properties> @@ -130,6 +131,11 @@ <version>${commons-io-version}</version> </dependency> <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-compress</artifactId> + <version>${commons-compress-version}</version> + </dependency> + <dependency> <groupId>com.hubspot.jinjava</groupId> <artifactId>jinjava</artifactId> <version>${jinja.version}</version> |