From 3f0da55c0183e9da2f4658308dee1915338266aa Mon Sep 17 00:00:00 2001 From: Lukasz Rajewski Date: Mon, 25 Oct 2021 22:21:16 +0200 Subject: Fix resolution of values for Config API Issue-ID: MULTICLOUD-1414 Signed-off-by: Lukasz Rajewski Change-Id: Ibca5846ffe083bfc4d505c4c3c13efac2c6e2426 --- src/k8splugin/internal/app/config_backend.go | 64 ++++++++++++++++------ src/k8splugin/internal/app/config_test.go | 4 +- src/k8splugin/internal/app/instance.go | 27 ++++++--- src/k8splugin/internal/helm/helm.go | 6 +- src/k8splugin/internal/helm/helm_test.go | 2 +- .../internal/namegenerator/namegenerator.go | 20 +++++++ src/k8splugin/internal/rb/profile.go | 31 +++++++---- 7 files changed, 112 insertions(+), 42 deletions(-) diff --git a/src/k8splugin/internal/app/config_backend.go b/src/k8splugin/internal/app/config_backend.go index 1f22922a..c365363f 100644 --- a/src/k8splugin/internal/app/config_backend.go +++ b/src/k8splugin/internal/app/config_backend.go @@ -461,7 +461,7 @@ func applyConfig(instanceID string, p Config, pChannel chan configResourceList, return []helm.KubernetesResource{}, pkgerrors.Wrap(err, "Retrieving model info") } // Get Template and Resolve the template with values - crl, err := resolve(rbName, rbVersion, profileName, p, releaseName) + crl, err := resolve(rbName, rbVersion, profileName, instanceID, p, releaseName) if err != nil { return []helm.KubernetesResource{}, pkgerrors.Wrap(err, "Resolve Config") } @@ -571,11 +571,12 @@ func scheduleResources(c chan configResourceList) { //Resolve returns the path where the helm chart merged with //configuration overrides resides. -var resolve = func(rbName, rbVersion, profileName string, p Config, releaseName string) (configResourceList, error) { +var resolve = func(rbName, rbVersion, profileName, instanceId string, p Config, releaseName string) (configResourceList, error) { var resTemplates []helm.KubernetesResourceTemplate - profile, err := rb.NewProfileClient().Get(rbName, rbVersion, profileName) + profileClient := rb.NewProfileClient() + profile, err := profileClient.Get(rbName, rbVersion, profileName) if err != nil { return configResourceList{}, pkgerrors.Wrap(err, "Reading Profile Data") } @@ -593,8 +594,42 @@ var resolve = func(rbName, rbVersion, profileName string, p Config, releaseName return configResourceList{}, pkgerrors.Wrap(err, "Downloading Template") } + ic := NewInstanceClient() + instance, err := ic.Get(instanceId) + if err != nil { + return configResourceList{}, pkgerrors.Wrap(err, "Getting Instance") + } + + var finalReleaseName string + + if releaseName == "" { + finalReleaseName = profile.ReleaseName + } else { + finalReleaseName = releaseName + } + + helmClient := helm.NewTemplateClient(profile.KubernetesVersion, + profile.Namespace, + finalReleaseName) + + //copy values from the instance + valuesArr := []string{} + if instance.Request.OverrideValues != nil { + for k, v := range instance.Request.OverrideValues { + valuesArr = append(valuesArr, k+"="+v) + } + } + valuesArr = append(valuesArr, "k8s-rb-instance-id="+instanceId) + rawValues, err := helmClient.ProcessValues([]string{}, valuesArr) + if err != nil { + return configResourceList{}, pkgerrors.Wrap(err, "Processing values") + } + + for k, v := range p.Values { + rawValues[k] = v + } //Create a temp file in the system temp folder for values input - b, err := json.Marshal(p.Values) + b, err := json.Marshal(rawValues) if err != nil { return configResourceList{}, pkgerrors.Wrap(err, "Error Marshalling config data") } @@ -613,26 +648,21 @@ var resolve = func(rbName, rbVersion, profileName string, p Config, releaseName } defer outputfile.Close() - chartBasePath, err := rb.ExtractTarBall(bytes.NewBuffer(def)) + //Download and process the profile first + //If everything seems okay, then download the config templates + prYamlClient, err := profileClient.GetYamlClient(rbName, rbVersion, profileName) if err != nil { - return configResourceList{}, pkgerrors.Wrap(err, "Extracting Template") + return configResourceList{}, pkgerrors.Wrap(err, "Processing Profile Manifest") } - var finalReleaseName string - - if releaseName == "" { - finalReleaseName = profile.ReleaseName - } else { - finalReleaseName = releaseName + chartBasePath, err := rb.ExtractTarBall(bytes.NewBuffer(def)) + if err != nil { + return configResourceList{}, pkgerrors.Wrap(err, "Extracting Template") } - helmClient := helm.NewTemplateClient(profile.KubernetesVersion, - profile.Namespace, - finalReleaseName) - chartPath := filepath.Join(chartBasePath, t.ChartName) resTemplates, crdList, _, err := helmClient.GenerateKubernetesArtifacts(chartPath, - []string{outputfile.Name()}, + []string{prYamlClient.GetValues(), outputfile.Name()}, nil) if err != nil { return configResourceList{}, pkgerrors.Wrap(err, "Generate final k8s yaml") diff --git a/src/k8splugin/internal/app/config_test.go b/src/k8splugin/internal/app/config_test.go index 0cc3c3ce..1aef6656 100644 --- a/src/k8splugin/internal/app/config_test.go +++ b/src/k8splugin/internal/app/config_test.go @@ -91,7 +91,7 @@ func TestCreateConfig(t *testing.T) { db.Etcd = testCase.mockdb db.DBconn = provideMockModelData(testCase.instanceID, testCase.rbName, testCase.rbVersion, testCase.profileName) - resolve = func(rbName, rbVersion, profileName string, p Config, releaseName string) (configResourceList, error) { + resolve = func(rbName, rbVersion, profileName, instanceId string, p Config, releaseName string) (configResourceList, error) { return configResourceList{}, nil } impl := NewConfigClient() @@ -204,7 +204,7 @@ func TestRollbackConfig(t *testing.T) { db.Etcd = testCase.mockdb db.DBconn = provideMockModelData(testCase.instanceID, testCase.rbName, testCase.rbVersion, testCase.profileName) - resolve = func(rbName, rbVersion, profileName string, p Config, releaseName string) (configResourceList, error) { + resolve = func(rbName, rbVersion, profileName, instanceID string, p Config, releaseName string) (configResourceList, error) { return configResourceList{}, nil } impl := NewConfigClient() diff --git a/src/k8splugin/internal/app/instance.go b/src/k8splugin/internal/app/instance.go index e50a59e5..71042f08 100644 --- a/src/k8splugin/internal/app/instance.go +++ b/src/k8splugin/internal/app/instance.go @@ -224,18 +224,21 @@ func (v *InstanceClient) Create(i InstanceRequest) (InstanceResponse, error) { postDeleteTimeout = 600 } + id := namegenerator.Generate() + + overrideValues = append(overrideValues, "k8s-rb-instance-id="+id) + //Execute the kubernetes create command sortedTemplates, crdList, hookList, releaseName, err := rb.NewProfileClient().Resolve(i.RBName, i.RBVersion, i.ProfileName, overrideValues, i.ReleaseName) if err != nil { + namegenerator.Release(id) return InstanceResponse{}, pkgerrors.Wrap(err, "Error resolving helm charts") } - // TODO: Only generate if id is not provided - id := namegenerator.Generate() - k8sClient := KubernetesClient{} err = k8sClient.Init(i.CloudRegion, id) if err != nil { + namegenerator.Release(id) return InstanceResponse{}, pkgerrors.Wrap(err, "Getting CloudRegion Information") } @@ -273,19 +276,21 @@ func (v *InstanceClient) Create(i InstanceRequest) (InstanceResponse, error) { PostDeleteTimeout: postDeleteTimeout, } + err = k8sClient.ensureNamespace(profile.Namespace) + if err != nil { + namegenerator.Release(id) + return InstanceResponse{}, pkgerrors.Wrap(err, "Creating Namespace") + } + key := InstanceKey{ ID: id, } err = db.DBconn.Create(v.storeName, key, v.tagInst, dbData) if err != nil { + namegenerator.Release(id) return InstanceResponse{}, pkgerrors.Wrap(err, "Creating Instance DB Entry") } - err = k8sClient.ensureNamespace(profile.Namespace) - if err != nil { - return InstanceResponse{}, pkgerrors.Wrap(err, "Creating Namespace") - } - if len(crdList) > 0 { log.Printf("Pre-Installing CRDs") _, err = k8sClient.createResources(crdList, profile.Namespace) @@ -303,6 +308,8 @@ func (v *InstanceClient) Create(i InstanceRequest) (InstanceResponse, error) { err2 := db.DBconn.Delete(v.storeName, key, v.tagInst) if err2 != nil { log.Printf("Error cleaning failed instance in DB, please check DB.") + } else { + namegenerator.Release(id) } return InstanceResponse{}, pkgerrors.Wrap(err, "Error running preinstall hooks") } @@ -314,6 +321,8 @@ func (v *InstanceClient) Create(i InstanceRequest) (InstanceResponse, error) { err2 := db.DBconn.Delete(v.storeName, key, v.tagInst) if err2 != nil { log.Printf("Delete Instance DB Entry for release %s has error.", releaseName) + } else { + namegenerator.Release(id) } return InstanceResponse{}, pkgerrors.Wrap(err, "Update Instance DB Entry") } @@ -330,6 +339,8 @@ func (v *InstanceClient) Create(i InstanceRequest) (InstanceResponse, error) { err2 := db.DBconn.Delete(v.storeName, key, v.tagInst) if err2 != nil { log.Printf("Delete Instance DB Entry for release %s has error.", releaseName) + } else { + namegenerator.Release(id) } return InstanceResponse{}, pkgerrors.Wrap(err, "Create Kubernetes Resources") } diff --git a/src/k8splugin/internal/helm/helm.go b/src/k8splugin/internal/helm/helm.go index b27c8aee..846366e4 100644 --- a/src/k8splugin/internal/helm/helm.go +++ b/src/k8splugin/internal/helm/helm.go @@ -72,9 +72,9 @@ func NewTemplateClient(k8sversion, namespace, releasename string) *TemplateClien } } -// Combines valueFiles and values into a single values stream. +// ProcessValues Combines valueFiles and values into a single values stream. // values takes precedence over valueFiles -func (h *TemplateClient) processValues(valueFiles []string, values []string) (map[string]interface{}, error) { +func (h *TemplateClient) ProcessValues(valueFiles []string, values []string) (map[string]interface{}, error) { settings := cli.New() providers := getter.All(settings) options := helmOptions.Options{ @@ -121,7 +121,7 @@ func (h *TemplateClient) GenerateKubernetesArtifacts(inputPath string, valueFile } // get combined values and create config - rawVals, err := h.processValues(valueFiles, values) + rawVals, err := h.ProcessValues(valueFiles, values) if err != nil { return retData, crdData, hookList, err } diff --git a/src/k8splugin/internal/helm/helm_test.go b/src/k8splugin/internal/helm/helm_test.go index 951ff92b..8f0269ab 100644 --- a/src/k8splugin/internal/helm/helm_test.go +++ b/src/k8splugin/internal/helm/helm_test.go @@ -95,7 +95,7 @@ func TestProcessValues(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.label, func(t *testing.T) { tc := NewTemplateClient("1.12.3", "testnamespace", "testreleasename") - out, err := tc.processValues(testCase.valueFiles, testCase.values) + out, err := tc.ProcessValues(testCase.valueFiles, testCase.values) if err != nil { if testCase.expectedError == "" { t.Fatalf("Got an error %s", err) diff --git a/src/k8splugin/internal/namegenerator/namegenerator.go b/src/k8splugin/internal/namegenerator/namegenerator.go index 52eef366..0a49633a 100644 --- a/src/k8splugin/internal/namegenerator/namegenerator.go +++ b/src/k8splugin/internal/namegenerator/namegenerator.go @@ -141,8 +141,28 @@ func (c *cache) generateName() string { } } +func (c *cache) releaseName(name string) { + c.mux.Lock() + defer c.mux.Unlock() + + c.init() + + if c.isAlreadyUsed(name) { + c.cache[name] = false + + // Update the cache and db + c.writeCacheToDB() + } +} + // Generate returns an autogenerated name func Generate() string { return nameCache.generateName() } + +// Release name from cache +func Release(name string) { + + nameCache.releaseName(name) +} diff --git a/src/k8splugin/internal/rb/profile.go b/src/k8splugin/internal/rb/profile.go index 77398580..ac90a63b 100644 --- a/src/k8splugin/internal/rb/profile.go +++ b/src/k8splugin/internal/rb/profile.go @@ -279,6 +279,25 @@ func (v *ProfileClient) Download(rbName, rbVersion, prName string) ([]byte, erro return nil, pkgerrors.New("Error downloading Profile content") } +//GetYamlClient GEt Yaml Files client for profile +func (v *ProfileClient) GetYamlClient(rbName string, rbVersion string, profileName string) (ProfileYamlClient, error) { + prData, err := v.Download(rbName, rbVersion, profileName) + if err != nil { + return ProfileYamlClient{}, pkgerrors.Wrap(err, "Downloading Profile") + } + + prPath, err := ExtractTarBall(bytes.NewBuffer(prData)) + if err != nil { + return ProfileYamlClient{}, pkgerrors.Wrap(err, "Extracting Profile Content") + } + + prYamlClient, err := ProcessProfileYaml(prPath, v.manifestName) + if err != nil { + return ProfileYamlClient{}, pkgerrors.Wrap(err, "Processing Profile Manifest") + } + return prYamlClient, nil +} + //Resolve returns the path where the helm chart merged with //configuration overrides resides and final ReleaseName picked for instantiation func (v *ProfileClient) Resolve(rbName string, rbVersion string, @@ -291,17 +310,7 @@ func (v *ProfileClient) Resolve(rbName string, rbVersion string, //Download and process the profile first //If everything seems okay, then download the definition - prData, err := v.Download(rbName, rbVersion, profileName) - if err != nil { - return sortedTemplates, crdList, hookList, finalReleaseName, pkgerrors.Wrap(err, "Downloading Profile") - } - - prPath, err := ExtractTarBall(bytes.NewBuffer(prData)) - if err != nil { - return sortedTemplates, crdList, hookList, finalReleaseName, pkgerrors.Wrap(err, "Extracting Profile Content") - } - - prYamlClient, err := ProcessProfileYaml(prPath, v.manifestName) + prYamlClient, err := v.GetYamlClient(rbName, rbVersion, profileName) if err != nil { return sortedTemplates, crdList, hookList, finalReleaseName, pkgerrors.Wrap(err, "Processing Profile Manifest") } -- cgit 1.2.3-korg