diff options
Diffstat (limited to 'src/k8splugin/internal')
-rw-r--r-- | src/k8splugin/internal/app/client.go | 181 | ||||
-rw-r--r-- | src/k8splugin/internal/app/client_test.go | 2 | ||||
-rw-r--r-- | src/k8splugin/internal/plugin/helpers.go | 92 |
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 +} |