7078b813267e674abb59ace4db8ee02aebaabe5a
[multicloud/k8s.git] / src / k8splugin / internal / plugin / helpers.go
1 /*
2  * Copyright 2019 Intel Corporation, Inc
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package plugin
18
19 import (
20         "log"
21         "strings"
22
23         utils "github.com/onap/multicloud-k8s/src/k8splugin/internal"
24         "github.com/onap/multicloud-k8s/src/k8splugin/internal/config"
25         "github.com/onap/multicloud-k8s/src/k8splugin/internal/helm"
26
27         pkgerrors "github.com/pkg/errors"
28         corev1 "k8s.io/api/core/v1"
29         "k8s.io/apimachinery/pkg/api/meta"
30         "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
31         "k8s.io/apimachinery/pkg/runtime"
32         "k8s.io/apimachinery/pkg/runtime/schema"
33         "k8s.io/client-go/dynamic"
34         "k8s.io/client-go/kubernetes"
35 )
36
37 // KubernetesConnector is an interface that is expected to be implemented
38 // by any code that calls the plugin framework functions.
39 // It implements methods that are needed by the plugins to get Kubernetes
40 // clients and other information needed to interface with Kubernetes
41 type KubernetesConnector interface {
42         //GetMapper returns the RESTMapper that was created for this client
43         GetMapper() meta.RESTMapper
44
45         //GetDynamicClient returns the dynamic client that is needed for
46         //unstructured REST calls to the apiserver
47         GetDynamicClient() dynamic.Interface
48
49         // GetStandardClient returns the standard client that can be used to handle
50         // standard kubernetes kinds
51         GetStandardClient() kubernetes.Interface
52
53         //GetInstanceID returns the InstanceID for tracking during creation
54         GetInstanceID() string
55 }
56
57 // Reference is the interface that is implemented
58 type Reference interface {
59         //Create a kubernetes resource described by the yaml in yamlFilePath
60         Create(yamlFilePath string, namespace string, client KubernetesConnector) (string, error)
61
62         //Get a kubernetes resource based on the groupVersionKind and resourceName provided in resource
63         Get(resource helm.KubernetesResource, namespace string, client KubernetesConnector) (string, error)
64
65         //List all resources of the specified GroupVersionKind in the given namespace
66         //If gvk is empty, the plugin will return all supported objects in the namespace
67         List(gvk schema.GroupVersionKind, namespace string, client KubernetesConnector) ([]helm.KubernetesResource, error)
68
69         //Delete a kubernetes resource described in the provided namespace
70         Delete(resource helm.KubernetesResource, namespace string, client KubernetesConnector) error
71
72         //Update kubernetes resource based on the groupVersionKind and resourceName provided in resource
73         Update(yamlFilePath string, namespace string, client KubernetesConnector) (string, error)
74 }
75
76 // GetPluginByKind returns a plugin by the kind name
77 // If plugin does not exist, it will return the generic plugin
78 // TODO: Change this once we have a plugin registration mechanism
79 func GetPluginByKind(kind string) (Reference, error) {
80
81         typePlugin, ok := utils.LoadedPlugins[strings.ToLower(kind)]
82         if !ok {
83                 log.Println("No plugin for kind " + kind + " found. Using generic Plugin")
84                 typePlugin, ok = utils.LoadedPlugins["generic"]
85                 if !ok {
86                         return nil, pkgerrors.New("No generic plugin found")
87                 }
88         }
89
90         symbol, err := typePlugin.Lookup("ExportedVariable")
91         if err != nil {
92                 return nil, pkgerrors.Wrap(err, "No ExportedVariable symbol found")
93         }
94
95         //Assert if it implements the PluginReference interface
96         pluginImpl, ok := symbol.(Reference)
97         if !ok {
98                 return nil, pkgerrors.New("ExportedVariable does not implement plugins.Reference interface type")
99         }
100
101         return pluginImpl, nil
102 }
103
104 // TagPodsIfPresent finds the PodTemplateSpec from any workload
105 // object that contains it and changes the spec to include the tag label
106 func TagPodsIfPresent(unstruct *unstructured.Unstructured, tag string) {
107
108         spec, ok := unstruct.Object["spec"].(map[string]interface{})
109         if !ok {
110                 log.Println("Error converting spec to map")
111                 return
112         }
113
114         template, ok := spec["template"].(map[string]interface{})
115         if !ok {
116                 log.Println("Error converting template to map")
117                 return
118         }
119
120         //Attempt to convert the template to a podtemplatespec.
121         //This is to check if we have any pods being created.
122         podTemplateSpec := &corev1.PodTemplateSpec{}
123         err := runtime.DefaultUnstructuredConverter.FromUnstructured(template, podTemplateSpec)
124         if err != nil {
125                 log.Println("Did not find a podTemplateSpec: " + err.Error())
126                 return
127         }
128
129         labels := podTemplateSpec.GetLabels()
130         if labels == nil {
131                 labels = map[string]string{}
132         }
133         labels[config.GetConfiguration().KubernetesLabelName] = tag
134         podTemplateSpec.SetLabels(labels)
135
136         updatedTemplate, err := runtime.DefaultUnstructuredConverter.ToUnstructured(podTemplateSpec)
137
138         //Set the label
139         spec["template"] = updatedTemplate
140 }