Retrieve original source for kustomize 21/1421/9
authorYolanda Robla <yroblamo@redhat.com>
Fri, 16 Aug 2019 14:18:45 +0000 (16:18 +0200)
committerYolanda Robla <yroblamo@redhat.com>
Thu, 5 Sep 2019 14:18:38 +0000 (16:18 +0200)
When patching content that is dynamically generated
and not being in kustomize, it cannot be done as it
misses the original source. Add a pre-processing in
apply_workloads, so it will retrieve all the original
data from cluster deploy, to make it available
to kustomize.

Signed-off-by: Yolanda Robla <yroblamo@redhat.com>
Change-Id: Ibd37801768fa2c45b89bdfa161838d843dd2c436

pkg/site/site.go
pkg/utils/utils.go

index 39f98a1..d0d8fc6 100644 (file)
@@ -406,25 +406,31 @@ func (s Site) ApplyWorkloads(kubeconfigFile string) {
                }
        }
        binariesPath := fmt.Sprintf("%s/requirements", siteBuildPath)
+       ocPath := fmt.Sprintf("%s/oc", binariesPath)
+       kustomizePath := fmt.Sprintf("%s/kustomize", binariesPath)
 
        // retrieve profile path and clone the repo
        _, profileLayerPath, profileRef := s.GetProfileFromSite()
        s.DownloadRepo(siteBuildPath, profileLayerPath, profileRef)
 
-       log.Println(fmt.Sprintf("Applying workloads from %s/blueprint/sites/site/02_cluster-addons", siteBuildPath))
-       out := utils.ApplyKustomize(fmt.Sprintf("%s/kustomize", binariesPath), fmt.Sprintf("%s/blueprint/sites/site/02_cluster-addons", siteBuildPath))
+       addonsPath := fmt.Sprintf("%s/blueprint/sites/site/02_cluster-addons", siteBuildPath)
+       log.Println(fmt.Sprintf("Applying workloads from %s", addonsPath))
+       utils.PrepareKustomize(ocPath, addonsPath, kubeconfigFile)
+       out := utils.ApplyKustomize(kustomizePath, addonsPath)
        if string(out) != "" {
                // now we can apply it
                utils.ApplyOc(fmt.Sprintf("%s/oc", binariesPath), out, kubeconfigFile)
        } else {
-               log.Println(fmt.Sprintf("No manifests found for %s/blueprint/sites/site/02_cluster-addons", siteBuildPath))
+               log.Println(fmt.Sprintf("No manifests found for %s", addonsPath))
        }
-       log.Println(fmt.Sprintf("Applying workloads from %s/blueprint/sites/site/03_services", siteBuildPath))
-       out = utils.ApplyKustomize(fmt.Sprintf("%s/kustomize", binariesPath), fmt.Sprintf("%s/blueprint/sites/site/03_services", siteBuildPath))
+       servicesPath := fmt.Sprintf("%s/blueprint/sites/site/03_services", siteBuildPath)
+       log.Println(fmt.Sprintf("Applying workloads from %s", servicesPath))
+       utils.PrepareKustomize(ocPath, servicesPath, kubeconfigFile)
+       out = utils.ApplyKustomize(kustomizePath, servicesPath)
        if string(out) != "" {
                // now we can apply it
-               utils.ApplyOc(fmt.Sprintf("%s/oc", binariesPath), out, kubeconfigFile)
+               utils.ApplyOc(ocPath, out, kubeconfigFile)
        } else {
-               log.Println(fmt.Sprintf("No manifests found for %s/blueprint/sites/site/03_services", siteBuildPath))
+               log.Println(fmt.Sprintf("No manifests found for %s", servicesPath))
        }
 }
index cd11002..58cbdca 100644 (file)
@@ -7,8 +7,12 @@ import (
        "log"
        "os"
        "os/exec"
+       "path"
        "path/filepath"
+       "reflect"
        "time"
+
+       "gopkg.in/yaml.v2"
 )
 
 // utility to validate pre-requisites for deploying
@@ -36,6 +40,130 @@ func ValidateRequirements(buildPath string, siteName string) {
 
 }
 
+// utility to get all kustomize dependencies before applying them
+func PrepareKustomize(kubectlBinary string, kustomizePath string, kubeconfigPath string) {
+       kustomizationContent, err := ioutil.ReadFile(fmt.Sprintf("%s/kustomization.yaml", kustomizePath))
+       if err != nil {
+               log.Println(fmt.Sprintf("Error reading kustomization content: %s", err))
+               os.Exit(1)
+       }
+       var kustomizationContentObj map[interface{}]interface{}
+       err = yaml.Unmarshal(kustomizationContent, &kustomizationContentObj)
+
+       // check if we have patchesjson entry
+       var patchesOutput [][]byte
+       if jsonPatches, ok := kustomizationContentObj["patchesJson6902"]; ok {
+               jsonList := reflect.ValueOf(jsonPatches)
+               var targetPatch map[interface{}]interface{}
+               var kindPatch interface{}
+               var namePatch interface{}
+               var namespacePatch interface{}
+               var groupPatch interface{}
+               var namespaceContent string
+
+               for i := 0; i < jsonList.Len(); i++ {
+                       currentPatch := jsonList.Index(i).Interface().(map[interface{}]interface{})
+                       targetPatch, ok = currentPatch["target"].(map[interface{}]interface{})
+                       if !ok {
+                               log.Fatal("Error parsing json patch, target not found")
+                               os.Exit(1)
+                       }
+                       if kindPatch, ok = targetPatch["kind"]; !ok {
+                               log.Fatal("Error parsing json patch, kind not found")
+                               os.Exit(1)
+                       }
+                       if namePatch, ok = targetPatch["name"]; !ok {
+                               log.Fatal("Error parsing json patch, name not found")
+                               os.Exit(1)
+                       }
+                       if namespacePatch, ok = targetPatch["namespace"]; !ok {
+                               namespaceContent = ""
+                       } else {
+                               namespaceContent = fmt.Sprintf("--namespace %s", namespacePatch)
+                       }
+
+                       if groupPatch, ok = targetPatch["group"]; ok {
+                               kindPatch = fmt.Sprintf("%s.%s", kindPatch, groupPatch)
+                       }
+
+                       // we have the signature of the patch, let's get the content
+                       var envVars[] string
+                       if len(kubeconfigPath)>0 {
+                               envVars = append(envVars, fmt.Sprintf("KUBECONFIG=%s", kubeconfigPath))
+                       }
+                       finalCommand := fmt.Sprintf("%s get %s/%s -o yaml %s", kubectlBinary, kindPatch, namePatch, namespaceContent)
+                       out, err := ExecuteCommand("", envVars, false, false, "/bin/bash", "-c", finalCommand)
+
+                       if len(err) > 0 {
+                               log.Println(fmt.Sprintf("Error extracting content from %s/%s", kindPatch, namePatch))
+                       } else {
+                               // if there is output, append to contents
+                               if out != nil {
+                                       patchesOutput = append(patchesOutput, out)
+                               }
+                       }
+               }
+       }
+
+       // if patchesOutput has content, create an entry in resources to inject the patches bit
+       if len(patchesOutput) > 0 {
+               resourcesPath := fmt.Sprintf("%s/patches_objects.yaml", kustomizePath)
+               os.Remove(resourcesPath)
+               f, err := os.Create(resourcesPath)
+               defer f.Close()
+               if err != nil {
+                       log.Fatal(fmt.Sprintf("Error creating patches file: %s", resourcesPath))
+                       os.Exit(1)
+               }
+
+               for _, patch := range patchesOutput {
+                       f.WriteString("---\n")
+                       f.Write(patch)
+               }
+
+               // if there is no resources entry, add it
+               if _, ok := kustomizationContentObj["resources"]; !ok {
+                       kustomizationContentObj["resources"] = make([]interface{}, 0)
+               }
+               resources := kustomizationContentObj["resources"]
+
+               // if entry does not exist, append it
+               found := false
+               for _, resourceValue := range resources.([]interface{}) {
+                       if resourceValue == resourcesPath {
+                               found = true
+                               break
+                       }
+               }
+               if !found {
+                       resources = append(resources.([]interface{}), resourcesPath)
+                       kustomizationContentObj["resources"] = resources
+               }
+               final, err := yaml.Marshal(&kustomizationContentObj)
+               if err != nil {
+                       log.Fatal("Error manipulating kustomization file")
+                       os.Exit(1)
+               }
+               // overwrite the original kustomization file
+               err = ioutil.WriteFile(fmt.Sprintf("%s/kustomization.yaml", kustomizePath), final, 0644)
+               if err != nil {
+                       log.Fatal(fmt.Sprintf("Error writing final kustomization file: %s", err))
+                       os.Exit(1)
+               }
+       }
+
+       //var basesOutput [][]byte
+       if bases, ok := kustomizationContentObj["bases"]; ok {
+               for _, baseValue := range bases.([]interface{}) {
+                       // convert to an absolute path
+                       absoluteBaseValue := path.Join(kustomizePath, baseValue.(string))
+
+                       // recursively call prepare kustomize
+                       PrepareKustomize(kubectlBinary, absoluteBaseValue, kubeconfigPath)
+               }
+       }
+}
+
 // utility to apply kustomize on a given directory
 func ApplyKustomize(kustomizeBinary string, kustomizePath string) []byte {
        // retrieve executable path to inject env var