From d23f21c5f5d27dc6b7b330e05d6e13fb197ded56 Mon Sep 17 00:00:00 2001 From: berkgokden Date: Mon, 6 May 2019 13:47:19 +0200 Subject: [PATCH] Added merge command related to LAM-244 --- cmd/merge.go | 85 +++++++++++++++++++++++++++++++++++++++++++++++ cmd/root.go | 2 +- go.mod | 3 +- util/util.go | 40 ++++++++++++++++++++++ util/util_test.go | 69 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 197 insertions(+), 2 deletions(-) create mode 100644 cmd/merge.go create mode 100644 util/util_test.go diff --git a/cmd/merge.go b/cmd/merge.go new file mode 100644 index 0000000..b12eafb --- /dev/null +++ b/cmd/merge.go @@ -0,0 +1,85 @@ +// Copyright © 2018 Developer developer@vamp.io +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cmd + +import ( + "errors" + "fmt" + + "github.com/magneticio/vampkubistcli/client" + "github.com/magneticio/vampkubistcli/logging" + "github.com/magneticio/vampkubistcli/util" + "github.com/spf13/cobra" +) + +// mergeCmd represents the merge command +var mergeCmd = &cobra.Command{ + Use: "merge", + Short: "Merges a resource", + Long: AddAppName(`To merge a resource +Run as $AppName merge resourceType ResourceName + +Example: + $AppName merge project myproject -f project.yaml + $AppName merge -p myproject cluster mycluster -f cluster.yaml`), + SilenceUsage: true, + SilenceErrors: true, + RunE: func(cmd *cobra.Command, args []string) error { + if len(args) < 2 { + return errors.New("Not Enough Arguments") + } + Type = args[0] + Name = args[1] + Source := SourceString + if Source == "" { + b, err := util.UseSourceUrl(SourceFile) // just pass the file name + if err != nil { + return err + } + Source = string(b) + } + + restClient := client.NewRestClient(Config.Url, Config.Token, Config.APIVersion, logging.Verbose, Config.Cert) + values := make(map[string]string) + values["project"] = Config.Project + values["cluster"] = Config.Cluster + values["virtual_cluster"] = Config.VirtualCluster + values["application"] = Application + spec, getSpecError := restClient.GetSpec(Type, Name, SourceFileType, values) + if getSpecError != nil { + return getSpecError + } + + updatedSource, mergeError := util.Merge(spec, Source, SourceFileType) + if mergeError != nil { + return mergeError + } + + isUpdated, updateError := restClient.Update(Type, Name, updatedSource, SourceFileType, values) + if !isUpdated { + return updateError + } + fmt.Println(Type + " " + Name + " is merged") + return nil + }, +} + +func init() { + rootCmd.AddCommand(mergeCmd) + + mergeCmd.Flags().StringVarP(&SourceString, "string", "s", "", "Source from string") + mergeCmd.Flags().StringVarP(&SourceFile, "file", "f", "", "Source from file") + mergeCmd.Flags().StringVarP(&SourceFileType, "input", "i", "yaml", "Resource file type yaml or json") +} diff --git a/cmd/root.go b/cmd/root.go index b14dd8f..91a1424 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -60,7 +60,7 @@ var Hosts []string var kubeConfigPath string // version should be in format d.d.d where d is a decimal number -const Version string = "v0.0.27" +const Version string = "v0.0.28" var AppName string = InitAppName() diff --git a/go.mod b/go.mod index e3ee96d..b904915 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/googleapis/gnostic v0.2.0 // indirect github.com/gophercloud/gophercloud v0.0.0-20190422143055-fe6299556848 // indirect github.com/gregjones/httpcache v0.0.0-20190212212710-3befbb6ad0cc // indirect - github.com/imdario/mergo v0.3.7 // indirect + github.com/imdario/mergo v0.3.7 github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/json-iterator/go v1.1.6 // indirect github.com/magneticio/forklift v0.1.15 @@ -23,6 +23,7 @@ require ( github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/spf13/cobra v0.0.3 github.com/spf13/viper v1.3.2 + github.com/stretchr/testify v1.2.2 github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 golang.org/x/crypto v0.0.0-20190422183909-d864b10871cd golang.org/x/net v0.0.0-20190420063019-afa5a82059c6 // indirect diff --git a/util/util.go b/util/util.go index aee7502..c285f72 100644 --- a/util/util.go +++ b/util/util.go @@ -19,6 +19,7 @@ import ( "time" "github.com/ghodss/yaml" + "github.com/imdario/mergo" "github.com/yalp/jsonpath" "golang.org/x/crypto/ssh/terminal" ) @@ -207,3 +208,42 @@ func RandomString(n int) string { return string(b) } + +func Merge(destination string, source string, serializationType string) (string, error) { + if serializationType == "yaml" { + var dst map[string]interface{} + var src map[string]interface{} + if marshallDstError := yaml.Unmarshal([]byte(destination), &dst); marshallDstError != nil { + return "", marshallDstError + } + if marshallSrcError := yaml.Unmarshal([]byte(source), &src); marshallSrcError != nil { + return "", marshallSrcError + } + if mergeError := mergo.Merge(&dst, src, mergo.WithOverride); mergeError != nil { + return "", mergeError + } + merged, marshallSpecError := yaml.Marshal(dst) + if marshallSpecError != nil { + return "", marshallSpecError + } + return string(merged), nil + } else if serializationType == "json" { + var dst map[string]interface{} + var src map[string]interface{} + if marshallDstError := json.Unmarshal([]byte(destination), &dst); marshallDstError != nil { + return "", marshallDstError + } + if marshallSrcError := json.Unmarshal([]byte(source), &src); marshallSrcError != nil { + return "", marshallSrcError + } + if mergeError := mergo.Merge(&dst, src, mergo.WithOverride); mergeError != nil { + return "", mergeError + } + merged, marshallSpecError := json.Marshal(dst) + if marshallSpecError != nil { + return "", marshallSpecError + } + return string(merged), nil + } + return "", errors.New("Serialization type is not supported") +} diff --git a/util/util_test.go b/util/util_test.go new file mode 100644 index 0000000..b9cb752 --- /dev/null +++ b/util/util_test.go @@ -0,0 +1,69 @@ +package util_test + +import ( + "testing" + + "github.com/magneticio/vampkubistcli/util" + "github.com/stretchr/testify/assert" +) + +func TestMergeBasic(t *testing.T) { + destination := + `metadata: + field1: value1 + field2: value2 + ` + source := + `metadata: + field1: value1updated + field3: value3 + ` + + expectedMerged := + `metadata: + field1: value1updated + field2: value2 + field3: value3 +` + serializationType := "yaml" + merged, err := util.Merge(destination, source, serializationType) + + assert.NoError(t, err) + assert.Equal(t, expectedMerged, merged) +} + +func TestMergeMapInMap(t *testing.T) { + destination := + `metadata: + field1: + value1map: + key1: innerValue1 + key2: 1 + key3: noUpdate + field2: value2 + ` + source := + `metadata: + field1: + value1map: + key1: innerValue1updated + key2: 2 + field3: value3 + ` + + expectedMerged := + `metadata: + field1: + value1map: + key1: innerValue1updated + key2: 2 + key3: noUpdate + field2: value2 + field3: value3 +` + serializationType := "yaml" + merged, err := util.Merge(destination, source, serializationType) + + assert.NoError(t, err) + assert.Equal(t, expectedMerged, merged) +}