summaryrefslogtreecommitdiffstats
path: root/src/k8splugin/internal/app
diff options
context:
space:
mode:
Diffstat (limited to 'src/k8splugin/internal/app')
-rw-r--r--src/k8splugin/internal/app/client.go190
-rw-r--r--src/k8splugin/internal/app/client_test.go52
-rw-r--r--src/k8splugin/internal/app/instance.go19
-rw-r--r--src/k8splugin/internal/app/instance_test.go168
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"
+ }
+ ]
+ }`),
},
},
}