summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/k8splugin/api/brokerhandler.go7
-rw-r--r--src/k8splugin/api/brokerhandler_test.go56
-rw-r--r--src/k8splugin/api/instancehandler_test.go74
-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
-rw-r--r--src/k8splugin/internal/helm/helm.go38
-rw-r--r--src/k8splugin/internal/helm/helm_test.go35
-rw-r--r--src/k8splugin/internal/helm/types.go41
-rw-r--r--src/k8splugin/internal/rb/config_backend.go30
-rw-r--r--src/k8splugin/internal/rb/profile.go32
-rw-r--r--src/k8splugin/plugins/generic/plugin.go28
13 files changed, 509 insertions, 261 deletions
diff --git a/src/k8splugin/api/brokerhandler.go b/src/k8splugin/api/brokerhandler.go
index 28e44231..80ab643c 100644
--- a/src/k8splugin/api/brokerhandler.go
+++ b/src/k8splugin/api/brokerhandler.go
@@ -19,6 +19,7 @@ import (
"net/http"
"k8splugin/internal/app"
+ "k8splugin/internal/helm"
"github.com/gorilla/mux"
)
@@ -44,9 +45,9 @@ type brokerRequest struct {
}
type brokerPOSTResponse struct {
- TemplateType string `json:"template_type"`
- WorkloadID string `json:"workload_id"`
- TemplateResponse map[string][]string `json:"template_response"`
+ TemplateType string `json:"template_type"`
+ WorkloadID string `json:"workload_id"`
+ TemplateResponse []helm.KubernetesResource `json:"template_response"`
}
type brokerGETResponse struct {
diff --git a/src/k8splugin/api/brokerhandler_test.go b/src/k8splugin/api/brokerhandler_test.go
index 57557ac8..d9991e68 100644
--- a/src/k8splugin/api/brokerhandler_test.go
+++ b/src/k8splugin/api/brokerhandler_test.go
@@ -24,8 +24,10 @@ import (
"testing"
"k8splugin/internal/app"
+ "k8splugin/internal/helm"
pkgerrors "github.com/pkg/errors"
+ "k8s.io/apimachinery/pkg/runtime/schema"
)
func TestBrokerCreateHandler(t *testing.T) {
@@ -68,9 +70,21 @@ func TestBrokerCreateHandler(t *testing.T) {
expected: brokerPOSTResponse{
WorkloadID: "HaKpys8e",
TemplateType: "heat",
- TemplateResponse: map[string][]string{
- "deployment": []string{"test-deployment"},
- "service": []string{"test-service"},
+ TemplateResponse: []helm.KubernetesResource{
+ {
+ GVK: schema.GroupVersionKind{
+ Group: "apps",
+ Version: "v1",
+ Kind: "Deployment"},
+ Name: "test-deployment",
+ },
+ {
+ GVK: schema.GroupVersionKind{
+ Group: "",
+ Version: "v1",
+ Kind: "Service"},
+ Name: "test-service",
+ },
},
},
expectedCode: http.StatusCreated,
@@ -83,9 +97,21 @@ func TestBrokerCreateHandler(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: "test-deployment",
+ },
+ {
+ GVK: schema.GroupVersionKind{
+ Group: "",
+ Version: "v1",
+ Kind: "Service"},
+ Name: "test-service",
+ },
},
},
},
@@ -154,9 +180,21 @@ func TestBrokerGetHandler(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: "test-deployment",
+ },
+ {
+ GVK: schema.GroupVersionKind{
+ Group: "",
+ Version: "v1",
+ Kind: "Service"},
+ Name: "test-service",
+ },
},
},
},
diff --git a/src/k8splugin/api/instancehandler_test.go b/src/k8splugin/api/instancehandler_test.go
index ed7135a6..6d2abf6f 100644
--- a/src/k8splugin/api/instancehandler_test.go
+++ b/src/k8splugin/api/instancehandler_test.go
@@ -24,9 +24,11 @@ import (
"testing"
"k8splugin/internal/app"
+ "k8splugin/internal/helm"
"github.com/gorilla/mux"
pkgerrors "github.com/pkg/errors"
+ "k8s.io/apimachinery/pkg/runtime/schema"
)
//Creating an embedded interface via anonymous variable
@@ -108,9 +110,21 @@ func TestInstanceCreateHandler(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: "test-deployment",
+ },
+ {
+ GVK: schema.GroupVersionKind{
+ Group: "",
+ Version: "v1",
+ Kind: "Service"},
+ Name: "test-service",
+ },
},
},
expectedCode: http.StatusCreated,
@@ -123,9 +137,21 @@ func TestInstanceCreateHandler(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: "test-deployment",
+ },
+ {
+ GVK: schema.GroupVersionKind{
+ Group: "",
+ Version: "v1",
+ Kind: "Service"},
+ Name: "test-service",
+ },
},
},
},
@@ -183,9 +209,21 @@ func TestInstanceGetHandler(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: "test-deployment",
+ },
+ {
+ GVK: schema.GroupVersionKind{
+ Group: "",
+ Version: "v1",
+ Kind: "Service"},
+ Name: "test-service",
+ },
},
},
instClient: &mockInstanceClient{
@@ -197,9 +235,21 @@ func TestInstanceGetHandler(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: "test-deployment",
+ },
+ {
+ GVK: schema.GroupVersionKind{
+ Group: "",
+ Version: "v1",
+ Kind: "Service"},
+ Name: "test-service",
+ },
},
},
},
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"
+ }
+ ]
+ }`),
},
},
}
diff --git a/src/k8splugin/internal/helm/helm.go b/src/k8splugin/internal/helm/helm.go
index 65a36d6b..1ab701ae 100644
--- a/src/k8splugin/internal/helm/helm.go
+++ b/src/k8splugin/internal/helm/helm.go
@@ -28,8 +28,10 @@ import (
"github.com/ghodss/yaml"
pkgerrors "github.com/pkg/errors"
-
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ "k8s.io/apimachinery/pkg/runtime/serializer/json"
"k8s.io/apimachinery/pkg/util/validation"
+ k8syaml "k8s.io/apimachinery/pkg/util/yaml"
"k8s.io/helm/pkg/chartutil"
"k8s.io/helm/pkg/manifest"
"k8s.io/helm/pkg/proto/hapi/chart"
@@ -143,10 +145,11 @@ func (h *TemplateClient) ensureDirectory(f string) error {
}
// GenerateKubernetesArtifacts a mapping of type to fully evaluated helm template
-func (h *TemplateClient) GenerateKubernetesArtifacts(inputPath string, valueFiles []string, values []string) (map[string][]string, error) {
+func (h *TemplateClient) GenerateKubernetesArtifacts(inputPath string, valueFiles []string,
+ values []string) ([]KubernetesResourceTemplate, error) {
var outputDir, chartPath, namespace, releaseName string
- var retData map[string][]string
+ var retData []KubernetesResourceTemplate
releaseName = h.releaseName
namespace = h.kubeNameSpace
@@ -226,7 +229,6 @@ func (h *TemplateClient) GenerateKubernetesArtifacts(inputPath string, valueFile
var manifestsToRender []manifest.Manifest
//render all manifests in the chart
manifestsToRender = listManifests
- retData = make(map[string][]string)
for _, m := range tiller.SortByKind(manifestsToRender) {
data := m.Content
b := filepath.Base(m.Name)
@@ -249,11 +251,31 @@ func (h *TemplateClient) GenerateKubernetesArtifacts(inputPath string, valueFile
return retData, err
}
- if val, ok := retData[m.Head.Kind]; ok {
- retData[m.Head.Kind] = append(val, mfilePath)
- } else {
- retData[m.Head.Kind] = []string{mfilePath}
+ gvk, err := getGroupVersionKind(data)
+ if err != nil {
+ return retData, err
}
+
+ kres := KubernetesResourceTemplate{
+ GVK: gvk,
+ FilePath: mfilePath,
+ }
+ retData = append(retData, kres)
}
return retData, nil
}
+
+func getGroupVersionKind(data string) (schema.GroupVersionKind, error) {
+ out, err := k8syaml.ToJSON([]byte(data))
+ if err != nil {
+ return schema.GroupVersionKind{}, pkgerrors.Wrap(err, "Converting yaml to json")
+ }
+
+ simpleMeta := json.SimpleMetaFactory{}
+ gvk, err := simpleMeta.Interpret(out)
+ if err != nil {
+ return schema.GroupVersionKind{}, pkgerrors.Wrap(err, "Parsing apiversion and kind")
+ }
+
+ return *gvk, nil
+}
diff --git a/src/k8splugin/internal/helm/helm_test.go b/src/k8splugin/internal/helm/helm_test.go
index 27bb9d79..a13c67ba 100644
--- a/src/k8splugin/internal/helm/helm_test.go
+++ b/src/k8splugin/internal/helm/helm_test.go
@@ -165,27 +165,26 @@ func TestGenerateKubernetesArtifacts(t *testing.T) {
} else {
//Compute the hash of returned data and compare
for _, v := range out {
- for _, f := range v {
- data, err := ioutil.ReadFile(f)
- if err != nil {
- t.Errorf("Unable to read file %s", v)
- }
- h.Write(data)
- gotHash := fmt.Sprintf("%x", h.Sum(nil))
- h.Reset()
+ f := v.FilePath
+ data, err := ioutil.ReadFile(f)
+ if err != nil {
+ t.Errorf("Unable to read file %s", v)
+ }
+ h.Write(data)
+ gotHash := fmt.Sprintf("%x", h.Sum(nil))
+ h.Reset()
- //Find the right hash from expectedHashMap
- expectedHash := ""
- for k1, v1 := range testCase.expectedHashMap {
- if strings.Contains(f, k1) == true {
- expectedHash = v1
- break
- }
- }
- if gotHash != expectedHash {
- t.Fatalf("Got unexpected hash for %s", f)
+ //Find the right hash from expectedHashMap
+ expectedHash := ""
+ for k1, v1 := range testCase.expectedHashMap {
+ if strings.Contains(f, k1) == true {
+ expectedHash = v1
+ break
}
}
+ if gotHash != expectedHash {
+ t.Fatalf("Got unexpected hash for %s", f)
+ }
}
}
})
diff --git a/src/k8splugin/internal/helm/types.go b/src/k8splugin/internal/helm/types.go
new file mode 100644
index 00000000..2c8badb8
--- /dev/null
+++ b/src/k8splugin/internal/helm/types.go
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2018 Intel Corporation, Inc
+ *
+ * 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 helm
+
+import (
+ "k8s.io/apimachinery/pkg/runtime/schema"
+)
+
+// Represents the template that is used to create a particular
+// resource in Kubernetes
+type KubernetesResourceTemplate struct {
+ // Tracks the apiVersion and Kind of the resource
+ GVK schema.GroupVersionKind
+ // Path to the file that contains the resource info
+ FilePath string
+}
+
+// KubernetesResource is the resource that is created in Kubernetes
+// It is the type that will be used for tracking a resource.
+// Any future information such as status, time can be added here
+// for tracking.
+type KubernetesResource struct {
+ // Tracks the apiVersion and Kind of the resource
+ GVK schema.GroupVersionKind
+ // Name of resource in Kubernetes
+ Name string
+}
diff --git a/src/k8splugin/internal/rb/config_backend.go b/src/k8splugin/internal/rb/config_backend.go
index b61fc493..e2fa5b3c 100644
--- a/src/k8splugin/internal/rb/config_backend.go
+++ b/src/k8splugin/internal/rb/config_backend.go
@@ -19,16 +19,16 @@ package rb
import (
"bytes"
"encoding/json"
- "k8splugin/internal/db"
- "k8splugin/internal/helm"
+ "io/ioutil"
"log"
+ "path/filepath"
"strconv"
"strings"
-
- "io/ioutil"
- "path/filepath"
"sync"
+ "k8splugin/internal/db"
+ "k8splugin/internal/helm"
+
"github.com/ghodss/yaml"
pkgerrors "github.com/pkg/errors"
)
@@ -56,9 +56,9 @@ type ConfigVersionStore struct {
}
type configResourceList struct {
- retmap map[string][]string
- profile Profile
- action string
+ resourceTemplates []helm.KubernetesResourceTemplate
+ profile Profile
+ action string
}
type profileDataManager struct {
@@ -341,13 +341,13 @@ func scheduleResources(c chan configResourceList) {
//TODO: ADD Check to see if Application running
switch {
case data.action == "POST":
- log.Printf("[scheduleResources]: POST %v %v", data.profile, data.retmap)
+ log.Printf("[scheduleResources]: POST %v %v", data.profile, data.resourceTemplates)
//TODO: Needs to add code to call Kubectl create
case data.action == "PUT":
- log.Printf("[scheduleResources]: PUT %v %v", data.profile, data.retmap)
+ log.Printf("[scheduleResources]: PUT %v %v", data.profile, data.resourceTemplates)
//TODO: Needs to add code to call Kubectl apply
case data.action == "DELETE":
- log.Printf("[scheduleResources]: DELETE %v %v", data.profile, data.retmap)
+ log.Printf("[scheduleResources]: DELETE %v %v", data.profile, data.resourceTemplates)
//TODO: Needs to add code to call Kubectl delete
}
@@ -358,7 +358,7 @@ func scheduleResources(c chan configResourceList) {
//configuration overrides resides.
var resolve = func(rbName, rbVersion, profileName string, p Config) (configResourceList, error) {
- var retMap map[string][]string
+ var resTemplates []helm.KubernetesResourceTemplate
profile, err := NewProfileClient().Get(rbName, rbVersion, profileName)
if err != nil {
@@ -408,15 +408,15 @@ var resolve = func(rbName, rbVersion, profileName string, p Config) (configResou
profile.ReleaseName)
chartPath := filepath.Join(chartBasePath, t.ChartName)
- retMap, err = helmClient.GenerateKubernetesArtifacts(chartPath,
+ resTemplates, err = helmClient.GenerateKubernetesArtifacts(chartPath,
[]string{outputfile.Name()},
nil)
if err != nil {
return configResourceList{}, pkgerrors.Wrap(err, "Generate final k8s yaml")
}
crl := configResourceList{
- retmap: retMap,
- profile: profile,
+ resourceTemplates: resTemplates,
+ profile: profile,
}
return crl, nil
diff --git a/src/k8splugin/internal/rb/profile.go b/src/k8splugin/internal/rb/profile.go
index 679815ac..7d3902f2 100644
--- a/src/k8splugin/internal/rb/profile.go
+++ b/src/k8splugin/internal/rb/profile.go
@@ -20,12 +20,12 @@ import (
"bytes"
"encoding/base64"
"encoding/json"
- "k8splugin/internal/db"
"path/filepath"
- pkgerrors "github.com/pkg/errors"
-
+ "k8splugin/internal/db"
"k8splugin/internal/helm"
+
+ pkgerrors "github.com/pkg/errors"
)
// Profile contains the parameters needed for resource bundle (rb) profiles
@@ -236,48 +236,48 @@ func (v *ProfileClient) Download(rbName, rbVersion, prName string) ([]byte, erro
//Resolve returns the path where the helm chart merged with
//configuration overrides resides.
func (v *ProfileClient) Resolve(rbName string, rbVersion string,
- profileName string, values []string) (map[string][]string, error) {
+ profileName string, values []string) ([]helm.KubernetesResourceTemplate, error) {
- var retMap map[string][]string
+ var sortedTemplates []helm.KubernetesResourceTemplate
//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 retMap, pkgerrors.Wrap(err, "Downloading Profile")
+ return sortedTemplates, pkgerrors.Wrap(err, "Downloading Profile")
}
prPath, err := ExtractTarBall(bytes.NewBuffer(prData))
if err != nil {
- return retMap, pkgerrors.Wrap(err, "Extracting Profile Content")
+ return sortedTemplates, pkgerrors.Wrap(err, "Extracting Profile Content")
}
prYamlClient, err := ProcessProfileYaml(prPath, v.manifestName)
if err != nil {
- return retMap, pkgerrors.Wrap(err, "Processing Profile Manifest")
+ return sortedTemplates, pkgerrors.Wrap(err, "Processing Profile Manifest")
}
definitionClient := NewDefinitionClient()
definition, err := definitionClient.Get(rbName, rbVersion)
if err != nil {
- return retMap, pkgerrors.Wrap(err, "Getting Definition Metadata")
+ return sortedTemplates, pkgerrors.Wrap(err, "Getting Definition Metadata")
}
defData, err := definitionClient.Download(rbName, rbVersion)
if err != nil {
- return retMap, pkgerrors.Wrap(err, "Downloading Definition")
+ return sortedTemplates, pkgerrors.Wrap(err, "Downloading Definition")
}
chartBasePath, err := ExtractTarBall(bytes.NewBuffer(defData))
if err != nil {
- return retMap, pkgerrors.Wrap(err, "Extracting Definition Charts")
+ return sortedTemplates, pkgerrors.Wrap(err, "Extracting Definition Charts")
}
//Get the definition ID and download its contents
profile, err := v.Get(rbName, rbVersion, profileName)
if err != nil {
- return retMap, pkgerrors.Wrap(err, "Getting Profile")
+ return sortedTemplates, pkgerrors.Wrap(err, "Getting Profile")
}
//Copy the profile configresources to the chart locations
@@ -287,7 +287,7 @@ func (v *ProfileClient) Resolve(rbName string, rbVersion string,
// chartpath: chart/config/resources/config.yaml
err = prYamlClient.CopyConfigurationOverrides(chartBasePath)
if err != nil {
- return retMap, pkgerrors.Wrap(err, "Copying configresources to chart")
+ return sortedTemplates, pkgerrors.Wrap(err, "Copying configresources to chart")
}
helmClient := helm.NewTemplateClient(profile.KubernetesVersion,
@@ -295,12 +295,12 @@ func (v *ProfileClient) Resolve(rbName string, rbVersion string,
profile.ReleaseName)
chartPath := filepath.Join(chartBasePath, definition.ChartName)
- retMap, err = helmClient.GenerateKubernetesArtifacts(chartPath,
+ sortedTemplates, err = helmClient.GenerateKubernetesArtifacts(chartPath,
[]string{prYamlClient.GetValues()},
values)
if err != nil {
- return retMap, pkgerrors.Wrap(err, "Generate final k8s yaml")
+ return sortedTemplates, pkgerrors.Wrap(err, "Generate final k8s yaml")
}
- return retMap, nil
+ return sortedTemplates, nil
}
diff --git a/src/k8splugin/plugins/generic/plugin.go b/src/k8splugin/plugins/generic/plugin.go
index b0cf609c..9073535c 100644
--- a/src/k8splugin/plugins/generic/plugin.go
+++ b/src/k8splugin/plugins/generic/plugin.go
@@ -24,22 +24,12 @@ import (
utils "k8splugin/internal"
"k8splugin/internal/app"
+ "k8splugin/internal/helm"
)
type genericPlugin struct {
}
-var kindToGVRMap = map[string]schema.GroupVersionResource{
- "ConfigMap": schema.GroupVersionResource{Group: "", Version: "v1", Resource: "configmaps"},
- "StatefulSet": schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "statefulsets"},
- "Job": schema.GroupVersionResource{Group: "batch", Version: "v1", Resource: "jobs"},
- "Pod": schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"},
- "DaemonSet": schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "daemonsets"},
- "CustomResourceDefinition": schema.GroupVersionResource{
- Group: "apiextensions.k8s.io", Version: "v1beta1", Resource: "customresourcedefinitions",
- },
-}
-
// Create deployment object in a specific Kubernetes cluster
func (g genericPlugin) Create(yamlFilePath string, namespace string, client *app.KubernetesClient) (string, error) {
if namespace == "" {
@@ -79,7 +69,7 @@ func (g genericPlugin) Create(yamlFilePath string, namespace string, client *app
}
// Delete an existing deployment hosted in a specific Kubernetes cluster
-func (g genericPlugin) Delete(kind string, name string, namespace string, client *app.KubernetesClient) error {
+func (g genericPlugin) Delete(resource helm.KubernetesResource, namespace string, client *app.KubernetesClient) error {
if namespace == "" {
namespace = "default"
}
@@ -90,14 +80,20 @@ func (g genericPlugin) Delete(kind string, name string, namespace string, client
}
dynClient := client.GetDynamicClient()
- gvr, ok := kindToGVRMap[kind]
- if !ok {
- return pkgerrors.New("GVR not found for: " + kind)
+ mapper := client.GetMapper()
+
+ mapping, err := mapper.RESTMapping(schema.GroupKind{
+ Group: resource.GVK.Group,
+ Kind: resource.GVK.Kind,
+ }, resource.GVK.Version)
+ if err != nil {
+ return pkgerrors.Wrap(err, "Mapping kind to resource error")
}
+ gvr := mapping.Resource
log.Printf("Using gvr: %s, %s, %s", gvr.Group, gvr.Version, gvr.Resource)
- err := dynClient.Resource(gvr).Namespace(namespace).Delete(name, opts)
+ err = dynClient.Resource(gvr).Namespace(namespace).Delete(resource.Name, opts)
if err != nil {
return pkgerrors.Wrap(err, "Delete object error")
}