aboutsummaryrefslogtreecommitdiffstats
path: root/src/k8splugin/internal
diff options
context:
space:
mode:
Diffstat (limited to 'src/k8splugin/internal')
-rw-r--r--src/k8splugin/internal/app/client.go181
-rw-r--r--src/k8splugin/internal/app/client_test.go2
-rw-r--r--src/k8splugin/internal/plugin/helpers.go92
3 files changed, 127 insertions, 148 deletions
diff --git a/src/k8splugin/internal/app/client.go b/src/k8splugin/internal/app/client.go
index 158d21de..9a5aa9e9 100644
--- a/src/k8splugin/internal/app/client.go
+++ b/src/k8splugin/internal/app/client.go
@@ -16,15 +16,16 @@ package app
import (
"log"
"os"
- "strings"
+ "time"
- utils "k8splugin/internal"
"k8splugin/internal/config"
"k8splugin/internal/connection"
"k8splugin/internal/helm"
+ "k8splugin/internal/plugin"
pkgerrors "github.com/pkg/errors"
"k8s.io/apimachinery/pkg/api/meta"
+ "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/discovery"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes"
@@ -32,16 +33,12 @@ import (
"k8s.io/client-go/tools/clientcmd"
)
-// PluginReference is the interface that is implemented
-type PluginReference interface {
- Create(yamlFilePath string, namespace string, client *KubernetesClient) (string, error)
- Delete(resource helm.KubernetesResource, namespace string, client *KubernetesClient) error
-}
-
+// KubernetesClient encapsulates the different clients' interfaces
+// we need when interacting with a Kubernetes cluster
type KubernetesClient struct {
- clientSet *kubernetes.Clientset
+ clientSet kubernetes.Interface
dynamicClient dynamic.Interface
- discoverClient *discovery.DiscoveryClient
+ discoverClient discovery.CachedDiscoveryInterface
restMapper meta.RESTMapper
}
@@ -86,40 +83,35 @@ func (k *KubernetesClient) init(cloudregion string) error {
return pkgerrors.Wrap(err, "Creating dynamic client")
}
- k.discoverClient, err = discovery.NewDiscoveryClientForConfig(config)
+ k.discoverClient, err = discovery.NewCachedDiscoveryClientForConfig(config, os.TempDir(), "", 10*time.Minute)
if err != nil {
return pkgerrors.Wrap(err, "Creating discovery client")
}
+ k.restMapper = restmapper.NewDeferredDiscoveryRESTMapper(k.discoverClient)
return nil
}
func (k *KubernetesClient) ensureNamespace(namespace string) error {
- namespacePlugin, ok := utils.LoadedPlugins["namespace"]
- if !ok {
- return pkgerrors.New("No plugin for namespace resource found")
- }
- symGetNamespaceFunc, err := namespacePlugin.Lookup("Get")
+ pluginImpl, err := plugin.GetPluginByKind("Namespace")
if err != nil {
- return pkgerrors.Wrap(err, "Error fetching get namespace function")
+ return pkgerrors.Wrap(err, "Loading Namespace Plugin")
}
- ns, _ := symGetNamespaceFunc.(func(string, string, kubernetes.Interface) (string, error))(
- namespace, namespace, k.clientSet)
+ ns, err := pluginImpl.Get(helm.KubernetesResource{
+ Name: namespace,
+ GVK: schema.GroupVersionKind{
+ Group: "",
+ Version: "v1",
+ Kind: "Namespace",
+ },
+ }, namespace, k)
if ns == "" {
log.Println("Creating " + namespace + " namespace")
- symGetNamespaceFunc, err := namespacePlugin.Lookup("Create")
- if err != nil {
- return pkgerrors.Wrap(err, "Error fetching create namespace plugin")
- }
- namespaceResource := &utils.ResourceData{
- Namespace: namespace,
- }
- _, err = symGetNamespaceFunc.(func(*utils.ResourceData, kubernetes.Interface) (string, error))(
- namespaceResource, k.clientSet)
+ _, err = pluginImpl.Create("", namespace, k)
if err != nil {
return pkgerrors.Wrap(err, "Error creating "+namespace+" namespace")
}
@@ -127,50 +119,6 @@ func (k *KubernetesClient) ensureNamespace(namespace string) error {
return nil
}
-func (k *KubernetesClient) createGeneric(resTempl helm.KubernetesResourceTemplate,
- namespace string) (helm.KubernetesResource, error) {
-
- log.Println("Processing Kind: " + resTempl.GVK.Kind)
-
- //Check if have the mapper before loading the plugin
- err := k.updateMapper()
- if err != nil {
- return helm.KubernetesResource{}, pkgerrors.Wrap(err, "Unable to create RESTMapper")
- }
-
- pluginObject, ok := utils.LoadedPlugins["generic"]
- if !ok {
- return helm.KubernetesResource{}, pkgerrors.New("No generic plugin found")
- }
-
- symbol, err := pluginObject.Lookup("ExportedVariable")
- if err != nil {
- return helm.KubernetesResource{}, pkgerrors.Wrap(err, "No ExportedVariable symbol found")
- }
-
- //Assert if it implements the PluginReference interface
- genericPlugin, ok := symbol.(PluginReference)
- if !ok {
- return helm.KubernetesResource{}, pkgerrors.New("ExportedVariable is not PluginReference type")
- }
-
- if _, err := os.Stat(resTempl.FilePath); os.IsNotExist(err) {
- return helm.KubernetesResource{}, pkgerrors.New("File " + resTempl.FilePath + "does not exists")
- }
-
- log.Println("Processing file: " + resTempl.FilePath)
-
- name, err := genericPlugin.Create(resTempl.FilePath, namespace, k)
- if err != nil {
- return helm.KubernetesResource{}, pkgerrors.Wrap(err, "Error in generic plugin")
- }
-
- return helm.KubernetesResource{
- GVK: resTempl.GVK,
- Name: name,
- }, nil
-}
-
func (k *KubernetesClient) createKind(resTempl helm.KubernetesResourceTemplate,
namespace string) (helm.KubernetesResource, error) {
@@ -182,28 +130,16 @@ func (k *KubernetesClient) createKind(resTempl helm.KubernetesResourceTemplate,
log.Println("Processing file: " + resTempl.FilePath)
- //Populate the namespace from profile instead of instance body
- genericKubeData := &utils.ResourceData{
- YamlFilePath: resTempl.FilePath,
- Namespace: namespace,
- }
-
- 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)
- }
-
- symCreateResourceFunc, err := typePlugin.Lookup("Create")
+ pluginImpl, err := plugin.GetPluginByKind(resTempl.GVK.Kind)
if err != nil {
- return helm.KubernetesResource{}, pkgerrors.Wrap(err, "Error fetching "+resTempl.GVK.Kind+" plugin")
+ return helm.KubernetesResource{}, pkgerrors.Wrap(err, "Error loading plugin")
}
- createdResourceName, err := symCreateResourceFunc.(func(*utils.ResourceData, kubernetes.Interface) (string, error))(
- genericKubeData, k.clientSet)
+ createdResourceName, err := pluginImpl.Create(resTempl.FilePath, namespace, k)
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,
@@ -231,58 +167,18 @@ func (k *KubernetesClient) createResources(sortedTemplates []helm.KubernetesReso
return createdResources, nil
}
-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")
- }
-
- //Assert that it implements the PluginReference interface
- genericPlugin, ok := symbol.(PluginReference)
- if !ok {
- return pkgerrors.New("ExportedVariable is not PluginReference type")
- }
-
- err = genericPlugin.Delete(resource, namespace, k)
- if err != nil {
- return pkgerrors.Wrap(err, "Error in generic plugin")
- }
-
- return nil
-}
-
func (k *KubernetesClient) deleteKind(resource helm.KubernetesResource, namespace string) error {
log.Println("Deleting Kind: " + resource.GVK.Kind)
- typePlugin, ok := utils.LoadedPlugins[strings.ToLower(resource.GVK.Kind)]
- if !ok {
- log.Println("No plugin for kind " + resource.GVK.Kind + " found. Using generic Plugin")
- return k.deleteGeneric(resource, namespace)
- }
-
- symDeleteResourceFunc, err := typePlugin.Lookup("Delete")
+ pluginImpl, err := plugin.GetPluginByKind(resource.GVK.Kind)
if err != nil {
- return pkgerrors.Wrap(err, "Error finding Delete symbol in plugin")
+ return pkgerrors.Wrap(err, "Error loading plugin")
}
log.Println("Deleting resource: " + resource.Name)
- err = symDeleteResourceFunc.(func(string, string, kubernetes.Interface) error)(
- resource.Name, namespace, k.clientSet)
+ err = pluginImpl.Delete(resource, namespace, k)
if err != nil {
- return pkgerrors.Wrap(err, "Error destroying "+resource.Name)
+ return pkgerrors.Wrap(err, "Error deleting "+resource.Name)
}
return nil
@@ -300,21 +196,6 @@ func (k *KubernetesClient) deleteResources(resources []helm.KubernetesResource,
return nil
}
-func (k *KubernetesClient) updateMapper() error {
- //Create restMapper if not already done
- if k.restMapper != nil {
- return nil
- }
-
- groupResources, err := restmapper.GetAPIGroupResources(k.discoverClient)
- if err != nil {
- return pkgerrors.Wrap(err, "Get GroupResources")
- }
-
- k.restMapper = restmapper.NewDiscoveryRESTMapper(groupResources)
- return nil
-}
-
//GetMapper returns the RESTMapper that was created for this client
func (k *KubernetesClient) GetMapper() meta.RESTMapper {
return k.restMapper
@@ -325,3 +206,9 @@ func (k *KubernetesClient) GetMapper() meta.RESTMapper {
func (k *KubernetesClient) GetDynamicClient() dynamic.Interface {
return k.dynamicClient
}
+
+// GetStandardClient returns the standard client that can be used to handle
+// standard kubernetes kinds
+func (k *KubernetesClient) GetStandardClient() kubernetes.Interface {
+ return k.clientSet
+}
diff --git a/src/k8splugin/internal/app/client_test.go b/src/k8splugin/internal/app/client_test.go
index 4bfbcb18..e52aa7f8 100644
--- a/src/k8splugin/internal/app/client_test.go
+++ b/src/k8splugin/internal/app/client_test.go
@@ -42,7 +42,7 @@ func LoadMockPlugins(krdLoadedPlugins map[string]*plugin.Plugin) error {
}
krdLoadedPlugins["namespace"] = mockPlugin
- krdLoadedPlugins["deployment"] = mockPlugin
+ krdLoadedPlugins["generic"] = mockPlugin
krdLoadedPlugins["service"] = mockPlugin
return nil
diff --git a/src/k8splugin/internal/plugin/helpers.go b/src/k8splugin/internal/plugin/helpers.go
new file mode 100644
index 00000000..efcd18c1
--- /dev/null
+++ b/src/k8splugin/internal/plugin/helpers.go
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2019 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 plugin
+
+import (
+ "log"
+ "strings"
+
+ utils "k8splugin/internal"
+ "k8splugin/internal/helm"
+
+ pkgerrors "github.com/pkg/errors"
+ "k8s.io/apimachinery/pkg/api/meta"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ "k8s.io/client-go/dynamic"
+ "k8s.io/client-go/kubernetes"
+)
+
+// KubernetesConnector is an interface that is expected to be implemented
+// by any code that calls the plugin framework functions.
+// It implements methods that are needed by the plugins to get Kubernetes
+// clients and other information needed to interface with Kubernetes
+type KubernetesConnector interface {
+ //GetMapper returns the RESTMapper that was created for this client
+ GetMapper() meta.RESTMapper
+
+ //GetDynamicClient returns the dynamic client that is needed for
+ //unstructured REST calls to the apiserver
+ GetDynamicClient() dynamic.Interface
+
+ // GetStandardClient returns the standard client that can be used to handle
+ // standard kubernetes kinds
+ GetStandardClient() kubernetes.Interface
+}
+
+// Reference is the interface that is implemented
+type Reference interface {
+ //Create a kubernetes resource described by the yaml in yamlFilePath
+ Create(yamlFilePath string, namespace string, client KubernetesConnector) (string, error)
+
+ //Get a kubernetes resource based on the groupVersionKind and resourceName provided in resource
+ Get(resource helm.KubernetesResource, namespace string, client KubernetesConnector) (string, error)
+
+ //List all resources of the specified GroupVersionKind in the given namespace
+ //If gvk is empty, the plugin will return all supported objects in the namespace
+ List(gvk schema.GroupVersionKind, namespace string, client KubernetesConnector) ([]helm.KubernetesResource, error)
+
+ //Delete a kubernetes resource described in the provided namespace
+ Delete(resource helm.KubernetesResource, namespace string, client KubernetesConnector) error
+}
+
+// GetPluginByKind returns a plugin by the kind name
+// If plugin does not exist, it will return the generic plugin
+// TODO: Change this once we have a plugin registration mechanism
+func GetPluginByKind(kind string) (Reference, error) {
+
+ typePlugin, ok := utils.LoadedPlugins[strings.ToLower(kind)]
+ if !ok {
+ log.Println("No plugin for kind " + kind + " found. Using generic Plugin")
+ typePlugin, ok = utils.LoadedPlugins["generic"]
+ if !ok {
+ return nil, pkgerrors.New("No generic plugin found")
+ }
+ }
+
+ symbol, err := typePlugin.Lookup("ExportedVariable")
+ if err != nil {
+ return nil, pkgerrors.Wrap(err, "No ExportedVariable symbol found")
+ }
+
+ //Assert if it implements the PluginReference interface
+ pluginImpl, ok := symbol.(Reference)
+ if !ok {
+ return nil, pkgerrors.New("ExportedVariable does not implement plugins.Reference interface type")
+ }
+
+ return pluginImpl, nil
+}