diff options
Diffstat (limited to 'src/k8splugin/internal/app')
-rw-r--r-- | src/k8splugin/internal/app/client.go | 190 | ||||
-rw-r--r-- | src/k8splugin/internal/app/client_test.go | 52 | ||||
-rw-r--r-- | src/k8splugin/internal/app/instance.go | 19 | ||||
-rw-r--r-- | src/k8splugin/internal/app/instance_test.go | 168 |
4 files changed, 265 insertions, 164 deletions
diff --git a/src/k8splugin/internal/app/client.go b/src/k8splugin/internal/app/client.go index 9b8873cc..7024420c 100644 --- a/src/k8splugin/internal/app/client.go +++ b/src/k8splugin/internal/app/client.go @@ -19,6 +19,7 @@ import ( "strings" utils "k8splugin/internal" + "k8splugin/internal/helm" pkgerrors "github.com/pkg/errors" "k8s.io/apimachinery/pkg/api/meta" @@ -27,13 +28,12 @@ import ( "k8s.io/client-go/kubernetes" "k8s.io/client-go/restmapper" "k8s.io/client-go/tools/clientcmd" - "k8s.io/helm/pkg/tiller" ) // PluginReference is the interface that is implemented type PluginReference interface { Create(yamlFilePath string, namespace string, client *KubernetesClient) (string, error) - Delete(kind string, name string, namespace string, client *KubernetesClient) error + Delete(resource helm.KubernetesResource, namespace string, client *KubernetesClient) error } type KubernetesClient struct { @@ -105,141 +105,124 @@ func (k *KubernetesClient) ensureNamespace(namespace string) error { return nil } -func (k *KubernetesClient) createGeneric(kind string, files []string, namespace string) ([]string, error) { +func (k *KubernetesClient) createGeneric(resTempl helm.KubernetesResourceTemplate, + namespace string) (helm.KubernetesResource, error) { - log.Println("Processing items of Kind: " + kind) + log.Println("Processing Kind: " + resTempl.GVK.Kind) //Check if have the mapper before loading the plugin err := k.updateMapper() if err != nil { - return nil, pkgerrors.Wrap(err, "Unable to create RESTMapper") + return helm.KubernetesResource{}, pkgerrors.Wrap(err, "Unable to create RESTMapper") } pluginObject, ok := utils.LoadedPlugins["generic"] if !ok { - return nil, pkgerrors.New("No generic plugin found") + return helm.KubernetesResource{}, pkgerrors.New("No generic plugin found") } symbol, err := pluginObject.Lookup("ExportedVariable") if err != nil { - return nil, pkgerrors.Wrap(err, "No ExportedVariable symbol found") + return helm.KubernetesResource{}, pkgerrors.Wrap(err, "No ExportedVariable symbol found") } //Assert if it implements the PluginReference interface genericPlugin, ok := symbol.(PluginReference) if !ok { - return nil, pkgerrors.New("ExportedVariable is not PluginReference type") + return helm.KubernetesResource{}, pkgerrors.New("ExportedVariable is not PluginReference type") } - //Iterate over each file of a particular kind here - var resourcesCreated []string - for _, f := range files { - if _, err := os.Stat(f); os.IsNotExist(err) { - return nil, pkgerrors.New("File " + f + "does not exists") - } - - log.Println("Processing file: " + f) + if _, err := os.Stat(resTempl.FilePath); os.IsNotExist(err) { + return helm.KubernetesResource{}, pkgerrors.New("File " + resTempl.FilePath + "does not exists") + } - name, err := genericPlugin.Create(f, namespace, k) - if err != nil { - return nil, pkgerrors.Wrap(err, "Error in generic plugin") - } + log.Println("Processing file: " + resTempl.FilePath) - resourcesCreated = append(resourcesCreated, name) + name, err := genericPlugin.Create(resTempl.FilePath, namespace, k) + if err != nil { + return helm.KubernetesResource{}, pkgerrors.Wrap(err, "Error in generic plugin") } - return resourcesCreated, nil -} -func (k *KubernetesClient) createKind(kind string, files []string, namespace string) ([]string, error) { + return helm.KubernetesResource{ + GVK: resTempl.GVK, + Name: name, + }, nil +} - log.Println("Processing items of Kind: " + kind) +func (k *KubernetesClient) createKind(resTempl helm.KubernetesResourceTemplate, + namespace string) (helm.KubernetesResource, error) { - //Iterate over each file of a particular kind here - var resourcesCreated []string - for _, f := range files { - if _, err := os.Stat(f); os.IsNotExist(err) { - return nil, pkgerrors.New("File " + f + "does not exists") - } + log.Println("Processing Kind: " + resTempl.GVK.Kind) - log.Println("Processing file: " + f) + if _, err := os.Stat(resTempl.FilePath); os.IsNotExist(err) { + return helm.KubernetesResource{}, pkgerrors.New("File " + resTempl.FilePath + "does not exists") + } - //Populate the namespace from profile instead of instance body - genericKubeData := &utils.ResourceData{ - YamlFilePath: f, - Namespace: namespace, - } + log.Println("Processing file: " + resTempl.FilePath) - typePlugin, ok := utils.LoadedPlugins[strings.ToLower(kind)] - if !ok { - log.Println("No plugin for kind " + kind + " found. Using generic Plugin") - return k.createGeneric(kind, files, namespace) - } + //Populate the namespace from profile instead of instance body + genericKubeData := &utils.ResourceData{ + YamlFilePath: resTempl.FilePath, + Namespace: namespace, + } - symCreateResourceFunc, err := typePlugin.Lookup("Create") - if err != nil { - return nil, pkgerrors.Wrap(err, "Error fetching "+kind+" plugin") - } + typePlugin, ok := utils.LoadedPlugins[strings.ToLower(resTempl.GVK.Kind)] + if !ok { + log.Println("No plugin for kind " + resTempl.GVK.Kind + " found. Using generic Plugin") + return k.createGeneric(resTempl, namespace) + } - createdResourceName, err := symCreateResourceFunc.(func(*utils.ResourceData, kubernetes.Interface) (string, error))( - genericKubeData, k.clientSet) - if err != nil { - return nil, pkgerrors.Wrap(err, "Error in plugin "+kind+" plugin") - } - log.Print(createdResourceName + " created") - resourcesCreated = append(resourcesCreated, createdResourceName) + symCreateResourceFunc, err := typePlugin.Lookup("Create") + if err != nil { + return helm.KubernetesResource{}, pkgerrors.Wrap(err, "Error fetching "+resTempl.GVK.Kind+" plugin") } - return resourcesCreated, nil + createdResourceName, err := symCreateResourceFunc.(func(*utils.ResourceData, kubernetes.Interface) (string, error))( + genericKubeData, k.clientSet) + if err != nil { + return helm.KubernetesResource{}, pkgerrors.Wrap(err, "Error in plugin "+resTempl.GVK.Kind+" plugin") + } + log.Print(createdResourceName + " created") + return helm.KubernetesResource{ + GVK: resTempl.GVK, + Name: createdResourceName, + }, nil } -func (k *KubernetesClient) createResources(resMap map[string][]string, - namespace string) (map[string][]string, error) { +func (k *KubernetesClient) createResources(sortedTemplates []helm.KubernetesResourceTemplate, + namespace string) ([]helm.KubernetesResource, error) { err := k.ensureNamespace(namespace) if err != nil { return nil, pkgerrors.Wrap(err, "Creating Namespace") } - createdResourceMap := make(map[string][]string) - // Create all the known kinds in the InstallOrder - for _, kind := range tiller.InstallOrder { - files, ok := resMap[kind] - if !ok { - log.Println("Kind " + kind + " not found. Skipping...") - continue - } - - resourcesCreated, err := k.createKind(kind, files, namespace) + var createdResources []helm.KubernetesResource + for _, resTempl := range sortedTemplates { + resCreated, err := k.createKind(resTempl, namespace) if err != nil { - return nil, pkgerrors.Wrap(err, "Error creating kind: "+kind) + return nil, pkgerrors.Wrapf(err, "Error creating kind: %+v", resTempl.GVK) } - - createdResourceMap[kind] = resourcesCreated - delete(resMap, kind) + createdResources = append(createdResources, resCreated) } - //Create the remaining kinds from the resMap - for kind, files := range resMap { - resourcesCreated, err := k.createKind(kind, files, namespace) - if err != nil { - return nil, pkgerrors.Wrap(err, "Error creating kind: "+kind) - } - - createdResourceMap[kind] = resourcesCreated - delete(resMap, kind) - } - - return createdResourceMap, nil + return createdResources, nil } -func (k *KubernetesClient) deleteGeneric(kind string, resources []string, namespace string) error { - log.Println("Deleting items of Kind: " + kind) +func (k *KubernetesClient) deleteGeneric(resource helm.KubernetesResource, namespace string) error { + log.Println("Deleting Kind: " + resource.GVK.Kind) pluginObject, ok := utils.LoadedPlugins["generic"] if !ok { return pkgerrors.New("No generic plugin found") } + //Check if have the mapper before loading the plugin + err := k.updateMapper() + if err != nil { + return pkgerrors.Wrap(err, "Unable to create RESTMapper") + } + symbol, err := pluginObject.Lookup("ExportedVariable") if err != nil { return pkgerrors.Wrap(err, "No ExportedVariable symbol found") @@ -251,45 +234,42 @@ func (k *KubernetesClient) deleteGeneric(kind string, resources []string, namesp return pkgerrors.New("ExportedVariable is not PluginReference type") } - for _, res := range resources { - err = genericPlugin.Delete(kind, res, namespace, k) - if err != nil { - return pkgerrors.Wrap(err, "Error in generic plugin") - } + err = genericPlugin.Delete(resource, namespace, k) + if err != nil { + return pkgerrors.Wrap(err, "Error in generic plugin") } return nil } -func (k *KubernetesClient) deleteKind(kind string, resources []string, namespace string) error { - log.Println("Deleting items of Kind: " + kind) +func (k *KubernetesClient) deleteKind(resource helm.KubernetesResource, namespace string) error { + log.Println("Deleting Kind: " + resource.GVK.Kind) - typePlugin, ok := utils.LoadedPlugins[strings.ToLower(kind)] + typePlugin, ok := utils.LoadedPlugins[strings.ToLower(resource.GVK.Kind)] if !ok { - log.Println("No plugin for kind " + kind + " found. Using generic Plugin") - return k.deleteGeneric(kind, resources, namespace) + log.Println("No plugin for kind " + resource.GVK.Kind + " found. Using generic Plugin") + return k.deleteGeneric(resource, namespace) } symDeleteResourceFunc, err := typePlugin.Lookup("Delete") if err != nil { - return pkgerrors.Wrap(err, "Error findinf Delete symbol in plugin") + return pkgerrors.Wrap(err, "Error finding Delete symbol in plugin") } - for _, res := range resources { - log.Println("Deleting resource: " + res) - err = symDeleteResourceFunc.(func(string, string, kubernetes.Interface) error)( - res, namespace, k.clientSet) - if err != nil { - return pkgerrors.Wrap(err, "Error destroying "+res) - } + log.Println("Deleting resource: " + resource.Name) + err = symDeleteResourceFunc.(func(string, string, kubernetes.Interface) error)( + resource.Name, namespace, k.clientSet) + if err != nil { + return pkgerrors.Wrap(err, "Error destroying "+resource.Name) } + return nil } -func (k *KubernetesClient) deleteResources(resMap map[string][]string, namespace string) error { +func (k *KubernetesClient) deleteResources(resources []helm.KubernetesResource, namespace string) error { //TODO: Investigate if deletion should be in a particular order - for kind, resourceNames := range resMap { - err := k.deleteKind(kind, resourceNames, namespace) + for _, res := range resources { + err := k.deleteKind(res, namespace) if err != nil { return pkgerrors.Wrap(err, "Deleting resources") } diff --git a/src/k8splugin/internal/app/client_test.go b/src/k8splugin/internal/app/client_test.go index b3436431..d023fcff 100644 --- a/src/k8splugin/internal/app/client_test.go +++ b/src/k8splugin/internal/app/client_test.go @@ -20,8 +20,10 @@ import ( "testing" utils "k8splugin/internal" + "k8splugin/internal/helm" pkgerrors "github.com/pkg/errors" + "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/kubernetes" ) @@ -76,9 +78,21 @@ func TestCreateResources(t *testing.T) { } t.Run("Successfully delete resources", func(t *testing.T) { - data := map[string][]string{ - "Deployment": []string{"../../mock_files/mock_yamls/deployment.yaml"}, - "Service": []string{"../../mock_files/mock_yamls/service.yaml"}, + data := []helm.KubernetesResourceTemplate{ + { + GVK: schema.GroupVersionKind{ + Group: "apps", + Version: "v1", + Kind: "Deployment"}, + FilePath: "../../mock_files/mock_yamls/deployment.yaml", + }, + { + GVK: schema.GroupVersionKind{ + Group: "", + Version: "v1", + Kind: "Service"}, + FilePath: "../../mock_files/mock_yamls/service.yaml", + }, } _, err := k8.createResources(data, "testnamespace") @@ -105,9 +119,35 @@ func TestDeleteResources(t *testing.T) { } t.Run("Successfully delete resources", func(t *testing.T) { - data := map[string][]string{ - "Deployment": []string{"deployment-1", "deployment-2"}, - "Service": []string{"service-1", "service-2"}, + data := []helm.KubernetesResource{ + { + GVK: schema.GroupVersionKind{ + Group: "apps", + Version: "v1", + Kind: "Deployment"}, + Name: "deployment-1", + }, + { + GVK: schema.GroupVersionKind{ + Group: "apps", + Version: "v1", + Kind: "Deployment"}, + Name: "deployment-2", + }, + { + GVK: schema.GroupVersionKind{ + Group: "", + Version: "v1", + Kind: "Service"}, + Name: "service-1", + }, + { + GVK: schema.GroupVersionKind{ + Group: "", + Version: "v1", + Kind: "Service"}, + Name: "service-2", + }, } err := k8.deleteResources(data, "test") diff --git a/src/k8splugin/internal/app/instance.go b/src/k8splugin/internal/app/instance.go index 93305c30..8e9a2b7a 100644 --- a/src/k8splugin/internal/app/instance.go +++ b/src/k8splugin/internal/app/instance.go @@ -23,6 +23,7 @@ import ( "os" "k8splugin/internal/db" + "k8splugin/internal/helm" "k8splugin/internal/rb" pkgerrors "github.com/pkg/errors" @@ -40,13 +41,13 @@ type InstanceRequest struct { // InstanceResponse contains the response from instantiation type InstanceResponse struct { - ID string `json:"id"` - RBName string `json:"rb-name"` - RBVersion string `json:"rb-version"` - ProfileName string `json:"profile-name"` - CloudRegion string `json:"cloud-region"` - Namespace string `json:"namespace"` - Resources map[string][]string `json:"resources"` + ID string `json:"id"` + RBName string `json:"rb-name"` + RBVersion string `json:"rb-version"` + ProfileName string `json:"profile-name"` + CloudRegion string `json:"cloud-region"` + Namespace string `json:"namespace"` + Resources []helm.KubernetesResource `json:"resources"` } // InstanceManager is an interface exposes the instantiation functionality @@ -113,7 +114,7 @@ func (v *InstanceClient) Create(i InstanceRequest) (InstanceResponse, error) { overrideValues := []string{} //Execute the kubernetes create command - resMap, err := rb.NewProfileClient().Resolve(i.RBName, i.RBVersion, i.ProfileName, overrideValues) + sortedTemplates, err := rb.NewProfileClient().Resolve(i.RBName, i.RBVersion, i.ProfileName, overrideValues) if err != nil { return InstanceResponse{}, pkgerrors.Wrap(err, "Error resolving helm charts") } @@ -124,7 +125,7 @@ func (v *InstanceClient) Create(i InstanceRequest) (InstanceResponse, error) { return InstanceResponse{}, pkgerrors.Wrap(err, "Getting CloudRegion Information") } - createdResources, err := k8sClient.createResources(resMap, profile.Namespace) + createdResources, err := k8sClient.createResources(sortedTemplates, profile.Namespace) if err != nil { return InstanceResponse{}, pkgerrors.Wrap(err, "Create Kubernetes Resources") } diff --git a/src/k8splugin/internal/app/instance_test.go b/src/k8splugin/internal/app/instance_test.go index effd5c99..3828ed38 100644 --- a/src/k8splugin/internal/app/instance_test.go +++ b/src/k8splugin/internal/app/instance_test.go @@ -21,7 +21,10 @@ import ( utils "k8splugin/internal" "k8splugin/internal/db" + "k8splugin/internal/helm" "k8splugin/internal/rb" + + "k8s.io/apimachinery/pkg/runtime/schema" ) func TestInstanceCreate(t *testing.T) { @@ -189,16 +192,32 @@ func TestInstanceGet(t *testing.T) { Items: map[string]map[string][]byte{ InstanceKey{ID: "HaKpys8e"}.String(): { "instance": []byte( - "{\"profile-name\":\"profile1\"," + - "\"id\":\"HaKpys8e\"," + - "\"namespace\":\"testnamespace\"," + - "\"rb-name\":\"test-rbdef\"," + - "\"rb-version\":\"v1\"," + - "\"cloud-region\":\"region1\"," + - "\"resources\": {" + - "\"deployment\": [\"test-deployment\"]," + - "\"service\": [\"test-service\"]" + - "}}"), + `{ + "profile-name":"profile1", + "id":"HaKpys8e", + "namespace":"testnamespace", + "rb-name":"test-rbdef", + "rb-version":"v1", + "cloud-region":"region1", + "resources": [ + { + "GVK": { + "Group":"apps", + "Version":"v1", + "Kind":"Deployment" + }, + "Name": "deployment-1" + }, + { + "GVK": { + "Group":"", + "Version":"v1", + "Kind":"Service" + }, + "Name": "service-1" + } + ] + }`), }, }, } @@ -210,16 +229,29 @@ func TestInstanceGet(t *testing.T) { ProfileName: "profile1", CloudRegion: "region1", Namespace: "testnamespace", - Resources: map[string][]string{ - "deployment": []string{"test-deployment"}, - "service": []string{"test-service"}, + + Resources: []helm.KubernetesResource{ + { + GVK: schema.GroupVersionKind{ + Group: "apps", + Version: "v1", + Kind: "Deployment"}, + Name: "deployment-1", + }, + { + GVK: schema.GroupVersionKind{ + Group: "", + Version: "v1", + Kind: "Service"}, + Name: "service-1", + }, }, } ic := NewInstanceClient() id := "HaKpys8e" data, err := ic.Get(id) if err != nil { - t.Fatalf("TestInstanceDelete returned an error (%s)", err) + t.Fatalf("TestInstanceGet returned an error (%s)", err) } if !reflect.DeepEqual(expected, data) { t.Fatalf("TestInstanceGet returned:\n result=%v\n expected=%v", @@ -232,16 +264,32 @@ func TestInstanceGet(t *testing.T) { Items: map[string]map[string][]byte{ InstanceKey{ID: "HaKpys8e"}.String(): { "instance": []byte( - "{\"profile-name\":\"profile1\"," + - "\"id\":\"HaKpys8e\"," + - "\"namespace\":\"testnamespace\"," + - "\"rb-name\":\"test-rbdef\"," + - "\"rb-version\":\"v1\"," + - "\"cloud-region\":\"mock_config\"," + - "\"resources\": {" + - "\"deployment\": [\"deployment-1\",\"deployment-2\"]," + - "\"service\": [\"service-1\",\"service-2\"]" + - "}}"), + `{ + "profile-name":"profile1", + "id":"HaKpys8e", + "namespace":"testnamespace", + "rb-name":"test-rbdef", + "rb-version":"v1", + "cloud-region":"region1", + "resources": [ + { + "GVK": { + "Group":"apps", + "Version":"v1", + "Kind":"Deployment" + }, + "Name": "deployment-1" + }, + { + "GVK": { + "Group":"", + "Version":"v1", + "Kind":"Service" + }, + "Name": "service-1" + } + ] + }`), }, }, } @@ -272,16 +320,32 @@ func TestInstanceDelete(t *testing.T) { Items: map[string]map[string][]byte{ InstanceKey{ID: "HaKpys8e"}.String(): { "instance": []byte( - "{\"profile-name\":\"profile1\"," + - "\"id\":\"HaKpys8e\"," + - "\"namespace\":\"testnamespace\"," + - "\"rb-name\":\"test-rbdef\"," + - "\"rb-version\":\"v1\"," + - "\"cloud-region\":\"mock_config\"," + - "\"resources\": {" + - "\"deployment\": [\"deployment-1\",\"deployment-2\"]," + - "\"service\": [\"service-1\",\"service-2\"]" + - "}}"), + `{ + "profile-name":"profile1", + "id":"HaKpys8e", + "namespace":"testnamespace", + "rb-name":"test-rbdef", + "rb-version":"v1", + "cloud-region":"mock_config", + "resources": [ + { + "GVK": { + "Group":"apps", + "Version":"v1", + "Kind":"Deployment" + }, + "Name": "deployment-1" + }, + { + "GVK": { + "Group":"", + "Version":"v1", + "Kind":"Service" + }, + "Name": "service-1" + } + ] + }`), }, }, } @@ -299,16 +363,32 @@ func TestInstanceDelete(t *testing.T) { Items: map[string]map[string][]byte{ InstanceKey{ID: "HaKpys8e"}.String(): { "instance": []byte( - "{\"profile-name\":\"profile1\"," + - "\"id\":\"HaKpys8e\"," + - "\"namespace\":\"testnamespace\"," + - "\"rb-name\":\"test-rbdef\"," + - "\"rb-version\":\"v1\"," + - "\"cloud-region\":\"mock_config\"," + - "\"resources\": {" + - "\"deployment\": [\"deployment-1\",\"deployment-2\"]," + - "\"service\": [\"service-1\",\"service-2\"]" + - "}}"), + `{ + "profile-name":"profile1", + "id":"HaKpys8e", + "namespace":"testnamespace", + "rb-name":"test-rbdef", + "rb-version":"v1", + "cloud-region":"mock_config", + "resources": [ + { + "GVK": { + "Group":"apps", + "Version":"v1", + "Kind":"Deployment" + }, + "Name": "deployment-1" + }, + { + "GVK": { + "Group":"", + "Version":"v1", + "Kind":"Service" + }, + "Name": "service-1" + } + ] + }`), }, }, } |