github.com/sap/cf-mta-plugin@v2.6.3+incompatible/util/archive_builder.go (about) 1 package util 2 3 import ( 4 "fmt" 5 "io" 6 "io/ioutil" 7 "os" 8 "path" 9 "path/filepath" 10 "strings" 11 12 "github.com/cloudfoundry-incubator/multiapps-cli-plugin/ui" 13 ) 14 15 const deploymentDescriptorYamlName string = "mtad.yaml" 16 17 // MtaArchiveBuilder builds mta archive 18 type MtaArchiveBuilder struct { 19 modules []string 20 resources []string 21 } 22 23 // NewMtaArchiveBuilder constructs new MtaArchiveBuilder 24 func NewMtaArchiveBuilder(modules, resources []string) MtaArchiveBuilder { 25 return MtaArchiveBuilder{ 26 modules: modules, 27 resources: resources, 28 } 29 } 30 31 // Build creates deployment archive from the provided deployment descriptor 32 func (builder MtaArchiveBuilder) Build(deploymentDescriptorLocation string) (string, error) { 33 descriptor, deploymentDescriptorFile, err := ParseDeploymentDescriptor(deploymentDescriptorLocation) 34 if err != nil { 35 return "", err 36 } 37 38 modulesPaths, err := builder.getModulesPaths(descriptor.Modules) 39 if err != nil { 40 return "", fmt.Errorf("Error building MTA Archive: %s", err.Error()) 41 } 42 resourcesPaths, err := builder.getResourcesPaths(descriptor.Resources) 43 if err != nil { 44 return "", fmt.Errorf("Error building MTA Archive: %s", err.Error()) 45 } 46 bindingParametersPaths := builder.getBindingParametersPaths(descriptor.Modules) 47 48 modulesSections := buildSection(normalizePathsUsingUnixSeparator(modulesPaths), MtaModule) 49 resourcesSections := buildSection(normalizePathsUsingUnixSeparator(resourcesPaths), MtaResource) 50 bindingParametersSections := buildSection(normalizePathsUsingUnixSeparator(bindingParametersPaths), MtaRequires) 51 52 manifestBuilder := NewMtaManifestBuilder() 53 manifestBuilder.ManifestSections(modulesSections) 54 manifestBuilder.ManifestSections(resourcesSections) 55 manifestBuilder.ManifestSections(bindingParametersSections) 56 manifestBuilder.ManifestSections([]ManifestSection{NewMtaManifestSectionBuilder().Name(MtadAttribute).Build()}) 57 58 manifestLocation, err := manifestBuilder.Build() 59 if err != nil { 60 return "", err 61 } 62 defer os.Remove(manifestLocation) 63 64 mtaAssembly, err := ioutil.TempDir("", "mta-assembly") 65 if err != nil { 66 return "", err 67 } 68 defer os.RemoveAll(mtaAssembly) 69 70 metaInfLocation := filepath.Join(mtaAssembly, "META-INF") 71 err = os.Mkdir(metaInfLocation, os.ModePerm) 72 if err != nil { 73 return "", err 74 } 75 76 manifestInfo, err := os.Stat(manifestLocation) 77 if err != nil { 78 return "", err 79 } 80 81 err = copyFile(manifestLocation, filepath.Join(metaInfLocation, manifestInfo.Name())) 82 if err != nil { 83 return "", err 84 } 85 86 err = copyFile(deploymentDescriptorFile, filepath.Join(metaInfLocation, "mtad.yaml")) 87 if err != nil { 88 return "", err 89 } 90 // TODO: modify the deployment descriptor after copying it in order not to contain any path parameters... 91 92 err = copyContent(deploymentDescriptorLocation, modulesPaths, mtaAssembly) 93 if err != nil { 94 return "", err 95 } 96 97 err = copyContent(deploymentDescriptorLocation, resourcesPaths, mtaAssembly) 98 if err != nil { 99 return "", err 100 } 101 102 err = copyContent(deploymentDescriptorLocation, bindingParametersPaths, mtaAssembly) 103 if err != nil { 104 return "", err 105 } 106 107 mtaArchiveName := descriptor.ID + ".mtar" 108 mtaArchiveLocation := filepath.Join(deploymentDescriptorLocation, mtaArchiveName) 109 err = CreateMtaArchive(mtaAssembly, mtaArchiveLocation) 110 if err != nil { 111 return "", err 112 } 113 114 mtaArchiveAbsolutePath, err := filepath.Abs(mtaArchiveLocation) 115 if err != nil { 116 return "", err 117 } 118 119 return mtaArchiveAbsolutePath, nil 120 } 121 122 func copyContent(sourceDirectory string, elementsPaths map[string]string, targetLocation string) error { 123 for name, path := range elementsPaths { 124 if path != "" { 125 sourceLocation := filepath.Join(sourceDirectory, path) 126 filesInSourceInfo, err := os.Stat(sourceLocation) 127 if err != nil { 128 return fmt.Errorf("Error building MTA Archive: file path %s not found", sourceLocation) 129 } 130 destinationLocation := filepath.Join(targetLocation, name, filepath.Base(path)) 131 if filesInSourceInfo.IsDir() { 132 err = copyDirectory(sourceLocation, destinationLocation) 133 } else { 134 os.MkdirAll(filepath.Dir(destinationLocation), os.ModePerm) 135 err = copyFile(sourceLocation, destinationLocation) 136 } 137 if err != nil { 138 return err 139 } 140 } 141 } 142 return nil 143 } 144 145 func copyDirectory(src, dest string) error { 146 var err error 147 var filesInDestinationInfo []os.FileInfo 148 var sourceInfo os.FileInfo 149 150 if sourceInfo, err = os.Stat(src); err != nil { 151 return err 152 } 153 154 if err = os.MkdirAll(dest, sourceInfo.Mode()); err != nil { 155 return err 156 } 157 158 if filesInDestinationInfo, err = ioutil.ReadDir(src); err != nil { 159 return err 160 } 161 for _, fd := range filesInDestinationInfo { 162 srcfp := path.Join(src, fd.Name()) 163 dstfp := path.Join(dest, fd.Name()) 164 165 if fd.IsDir() { 166 if err = copyDirectory(srcfp, dstfp); err != nil { 167 return err 168 } 169 } else { 170 if err = copyFile(srcfp, dstfp); err != nil { 171 return err 172 } 173 } 174 } 175 return nil 176 } 177 178 func copyFile(src, dest string) error { 179 fileFrom, err := os.Open(src) 180 if err != nil { 181 return err 182 } 183 defer fileFrom.Close() 184 185 toFile, err := os.OpenFile(dest, os.O_RDWR|os.O_CREATE, 0666) 186 if err != nil { 187 return err 188 } 189 defer toFile.Close() 190 191 _, err = io.Copy(toFile, fileFrom) 192 if err != nil { 193 return err 194 } 195 196 return nil 197 } 198 199 func buildSection(elements map[string]string, locatorName string) []ManifestSection { 200 result := make([]ManifestSection, 0) 201 for key, value := range concatenateElementsWithSameValue(elements) { 202 manifestSectionBuilder := NewMtaManifestSectionBuilder() 203 manifestSectionBuilder.Name(key) 204 manifestSectionBuilder.Attribute(locatorName, strings.Join(value, ",")) 205 result = append(result, manifestSectionBuilder.Build()) 206 } 207 return result 208 } 209 210 func concatenateElementsWithSameValue(elements map[string]string) map[string][]string { 211 result := make(map[string][]string) 212 for key, value := range elements { 213 if value == "" { 214 continue 215 } 216 if len(result[value]) != 0 { 217 result[value] = append(result[value], key) 218 } else { 219 result[value] = []string{key} 220 } 221 } 222 return result 223 } 224 225 func (builder MtaArchiveBuilder) getBindingParametersPaths(deploymentDescriptorResources []Module) map[string]string { 226 result := make(map[string]string, 0) 227 modulesToAdd := filterModules(deploymentDescriptorResources, func(module Module) bool { 228 return Contains(builder.modules, module.Name) 229 }) 230 for _, module := range modulesToAdd { 231 requiredDependenciesConfigPaths := getRequiredDependenciesConfigPaths(module.RequiredDependencies) 232 for requiredDependencyName, configFile := range requiredDependenciesConfigPaths { 233 result[module.Name+"/"+requiredDependencyName] = configFile 234 } 235 } 236 return result 237 } 238 239 func getRequiredDependenciesConfigPaths(requiredDependencies []RequiredDependency) map[string]string { 240 result := make(map[string]string, 0) 241 for _, requiredDependency := range requiredDependencies { 242 if requiredDependency.Parameters["path"] != nil { 243 result[requiredDependency.Name] = getString(requiredDependency.Parameters["path"]) 244 } 245 } 246 return result 247 } 248 249 func (builder MtaArchiveBuilder) getResourcesPaths(deploymentDescriptorResources []Resource) (map[string]string, error) { 250 err := validateSpecifiedResources(builder.resources, deploymentDescriptorResources) 251 if err != nil { 252 return nil, err 253 } 254 result := make(map[string]string) 255 resourcesToAdd := filterResources(deploymentDescriptorResources, func(resource Resource) bool { 256 return Contains(builder.resources, resource.Name) 257 }) 258 259 for _, resource := range resourcesToAdd { 260 result[resource.Name] = getString(resource.Parameters["path"]) 261 } 262 return result, nil 263 } 264 265 func validateSpecifiedResources(resourcesForDeployment []string, deploymentDescriptorResources []Resource) error { 266 deploymentDescriptorResourceNames := make([]string, 0) 267 for _, deploymentDescriptorResource := range deploymentDescriptorResources { 268 deploymentDescriptorResourceNames = append(deploymentDescriptorResourceNames, deploymentDescriptorResource.Name) 269 } 270 specifiedResourcesNotPartOfDeploymentDescriptor := make([]string, 0) 271 for _, resourceForDeployment := range resourcesForDeployment { 272 if !Contains(deploymentDescriptorResourceNames, resourceForDeployment) { 273 specifiedResourcesNotPartOfDeploymentDescriptor = append(specifiedResourcesNotPartOfDeploymentDescriptor, resourceForDeployment) 274 } 275 } 276 277 if len(specifiedResourcesNotPartOfDeploymentDescriptor) == 0 { 278 return nil 279 } 280 281 return fmt.Errorf("Resources %s are specified for deployment but are not part of deployment descriptor resources", strings.Join(specifiedResourcesNotPartOfDeploymentDescriptor, ", ")) 282 } 283 284 func getString(value interface{}) string { 285 if value == nil { 286 return "" 287 } 288 return value.(string) 289 } 290 291 func (builder MtaArchiveBuilder) getModulesPaths(deploymentDescriptorResources []Module) (map[string]string, error) { 292 err := validateSpecifiedModules(builder.modules, deploymentDescriptorResources) 293 if err != nil { 294 return nil, err 295 } 296 modulesToAdd := filterModules(deploymentDescriptorResources, func(module Module) bool { 297 return Contains(builder.modules, module.Name) 298 }) 299 specifiedModulesWithoutPaths := filterModules(modulesToAdd, func(module Module) bool { 300 return module.Path == "" 301 }) 302 moduleNamesWithoutPaths := make([]string, 0) 303 for _, moduleWithoutPath := range specifiedModulesWithoutPaths { 304 moduleNamesWithoutPaths = append(moduleNamesWithoutPaths, moduleWithoutPath.Name) 305 } 306 if len(moduleNamesWithoutPaths) > 0 { 307 ui.Warn("Modules %s do not have a path, specified for their binaries and will be ignored", strings.Join(moduleNamesWithoutPaths, ", ")) 308 } 309 result := make(map[string]string) 310 for _, moduleToAdd := range modulesToAdd { 311 if moduleToAdd.Path != "" { 312 result[moduleToAdd.Name] = moduleToAdd.Path 313 } 314 } 315 316 return result, nil 317 } 318 319 func normalizePathsUsingUnixSeparator(elementsPaths map[string]string) map[string]string { 320 normalizedPaths := make(map[string]string, len(elementsPaths)) 321 for key, value := range elementsPaths { 322 if value != "" { 323 zipEntryNormalizedPath := fmt.Sprintf("%s/%s", key, filepath.Base(value)) 324 normalizedPaths[key] = zipEntryNormalizedPath 325 } 326 } 327 return normalizedPaths 328 } 329 330 func validateSpecifiedModules(modulesForDeployment []string, deploymentDescriptorResources []Module) error { 331 deploymentDescriptorResourceNames := make([]string, 0) 332 for _, deploymentDescriptorResource := range deploymentDescriptorResources { 333 deploymentDescriptorResourceNames = append(deploymentDescriptorResourceNames, deploymentDescriptorResource.Name) 334 } 335 specifiedResourcesNotPartOfDeploymentDescriptor := make([]string, 0) 336 for _, moduleForDeployment := range modulesForDeployment { 337 if !Contains(deploymentDescriptorResourceNames, moduleForDeployment) { 338 specifiedResourcesNotPartOfDeploymentDescriptor = append(specifiedResourcesNotPartOfDeploymentDescriptor, moduleForDeployment) 339 } 340 } 341 342 if len(specifiedResourcesNotPartOfDeploymentDescriptor) == 0 { 343 return nil 344 } 345 346 return fmt.Errorf("Modules %s are specified for deployment but are not part of deployment descriptor modules", strings.Join(specifiedResourcesNotPartOfDeploymentDescriptor, ", ")) 347 } 348 349 func filterModules(modulesSlice []Module, prediacte func(element Module) bool) []Module { 350 result := make([]Module, 0) 351 for _, sliceElement := range modulesSlice { 352 if prediacte(sliceElement) { 353 result = append(result, sliceElement) 354 } 355 } 356 return result 357 } 358 359 func filterResources(resourcesSlice []Resource, predicate func(element Resource) bool) []Resource { 360 result := make([]Resource, 0) 361 for _, sliceElement := range resourcesSlice { 362 if predicate(sliceElement) { 363 result = append(result, sliceElement) 364 } 365 } 366 return result 367 } 368 369 func Contains(slice []string, element string) bool { 370 for _, sliceElement := range slice { 371 if sliceElement == element { 372 return true 373 } 374 } 375 return false 376 }