aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKiran Kamineni <kiran.k.kamineni@intel.com>2019-04-16 18:09:13 -0700
committerKiran Kamineni <kiran.k.kamineni@intel.com>2019-04-16 18:09:27 -0700
commitbf49d552b003072c6bc64ae838a4699c1f4028bd (patch)
tree57c2b6130781f8215b0544e6b6f126ec8e2f8152
parent244578803033f17781b10be283aef43fa6f0aa60 (diff)
Replace Kind with GroupVersionKind
Kind is not unique to track resources in Kubernetes GroupVersionKind is unique. We are just using that to track our data. It is abstracted behind a couple of new types for templates and resources. This change makes a lot of the old kind based operations redundant and simplified. Issue-ID: MULTICLOUD-573 Change-Id: I8f4ded2ba6a0821a8fbd679dc99ce3a44d805524 Signed-off-by: Kiran Kamineni <kiran.k.kamineni@intel.com>
-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")
}