2 Copyright 2018 Intel Corporation.
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6 http://www.apache.org/licenses/LICENSE-2.0
7 Unless required by applicable law or agreed to in writing, software
8 distributed under the License is distributed on an "AS IS" BASIS,
9 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 See the License for the specific language governing permissions and
11 limitations under the License.
21 "github.com/onap/multicloud-k8s/src/k8splugin/internal/connection"
22 "github.com/onap/multicloud-k8s/src/k8splugin/internal/helm"
23 log "github.com/onap/multicloud-k8s/src/k8splugin/internal/logutils"
24 "github.com/onap/multicloud-k8s/src/k8splugin/internal/plugin"
26 pkgerrors "github.com/pkg/errors"
27 "k8s.io/apimachinery/pkg/api/meta"
28 "k8s.io/apimachinery/pkg/runtime/schema"
29 "k8s.io/client-go/discovery/cached/disk"
30 "k8s.io/client-go/dynamic"
31 "k8s.io/client-go/kubernetes"
32 "k8s.io/client-go/restmapper"
33 "k8s.io/client-go/tools/clientcmd"
36 // KubernetesClient encapsulates the different clients' interfaces
37 // we need when interacting with a Kubernetes cluster
38 type KubernetesClient struct {
39 clientSet kubernetes.Interface
40 dynamicClient dynamic.Interface
41 discoverClient *disk.CachedDiscoveryClient
42 restMapper meta.RESTMapper
46 // getKubeConfig uses the connectivity client to get the kubeconfig based on the name
47 // of the cloudregion. This is written out to a file.
48 func (k *KubernetesClient) getKubeConfig(cloudregion string) (string, error) {
50 conn := connection.NewConnectionClient()
51 kubeConfigPath, err := conn.Download(cloudregion)
53 return "", pkgerrors.Wrap(err, "Downloading kubeconfig")
56 return kubeConfigPath, nil
59 // init loads the Kubernetes configuation values stored into the local configuration file
60 func (k *KubernetesClient) init(cloudregion string, iid string) error {
61 if cloudregion == "" {
62 return pkgerrors.New("Cloudregion is empty")
66 return pkgerrors.New("Instance ID is empty")
71 configPath, err := k.getKubeConfig(cloudregion)
73 return pkgerrors.Wrap(err, "Get kubeconfig file")
76 //Remove kubeconfigfile after the clients are created
77 defer os.Remove(configPath)
79 config, err := clientcmd.BuildConfigFromFlags("", configPath)
81 return pkgerrors.Wrap(err, "setConfig: Build config from flags raised an error")
84 k.clientSet, err = kubernetes.NewForConfig(config)
89 k.dynamicClient, err = dynamic.NewForConfig(config)
91 return pkgerrors.Wrap(err, "Creating dynamic client")
94 k.discoverClient, err = disk.NewCachedDiscoveryClientForConfig(config, os.TempDir(), "", 10*time.Minute)
96 return pkgerrors.Wrap(err, "Creating discovery client")
99 k.restMapper = restmapper.NewDeferredDiscoveryRESTMapper(k.discoverClient)
104 func (k *KubernetesClient) ensureNamespace(namespace string) error {
106 pluginImpl, err := plugin.GetPluginByKind("Namespace")
108 return pkgerrors.Wrap(err, "Loading Namespace Plugin")
111 ns, err := pluginImpl.Get(helm.KubernetesResource{
113 GVK: schema.GroupVersionKind{
120 // Check for errors getting the namespace while ignoring errors where the namespace does not exist
121 // Error message when namespace does not exist: "namespaces "namespace-name" not found"
122 if err != nil && strings.Contains(err.Error(), "not found") == false {
123 log.Error("Error checking for namespace", log.Fields{
125 "namespace": namespace,
127 return pkgerrors.Wrap(err, "Error checking for namespace: "+namespace)
131 log.Info("Creating Namespace", log.Fields{
132 "namespace": namespace,
135 _, err = pluginImpl.Create("", namespace, k)
137 log.Error("Error Creating Namespace", log.Fields{
139 "namespace": namespace,
141 return pkgerrors.Wrap(err, "Error creating "+namespace+" namespace")
147 func (k *KubernetesClient) createKind(resTempl helm.KubernetesResourceTemplate,
148 namespace string) (helm.KubernetesResource, error) {
150 if _, err := os.Stat(resTempl.FilePath); os.IsNotExist(err) {
151 return helm.KubernetesResource{}, pkgerrors.New("File " + resTempl.FilePath + "does not exists")
154 log.Info("Processing Kubernetes Resource", log.Fields{
155 "filepath": resTempl.FilePath,
158 pluginImpl, err := plugin.GetPluginByKind(resTempl.GVK.Kind)
160 return helm.KubernetesResource{}, pkgerrors.Wrap(err, "Error loading plugin")
163 createdResourceName, err := pluginImpl.Create(resTempl.FilePath, namespace, k)
165 log.Error("Error Creating Resource", log.Fields{
168 "filepath": resTempl.FilePath,
170 return helm.KubernetesResource{}, pkgerrors.Wrap(err, "Error in plugin "+resTempl.GVK.Kind+" plugin")
173 log.Info("Created Kubernetes Resource", log.Fields{
174 "resource": createdResourceName,
178 return helm.KubernetesResource{
180 Name: createdResourceName,
184 func (k *KubernetesClient) createResources(sortedTemplates []helm.KubernetesResourceTemplate,
185 namespace string) ([]helm.KubernetesResource, error) {
187 err := k.ensureNamespace(namespace)
189 return nil, pkgerrors.Wrap(err, "Creating Namespace")
192 var createdResources []helm.KubernetesResource
193 for _, resTempl := range sortedTemplates {
194 resCreated, err := k.createKind(resTempl, namespace)
196 return nil, pkgerrors.Wrapf(err, "Error creating kind: %+v", resTempl.GVK)
198 createdResources = append(createdResources, resCreated)
201 return createdResources, nil
204 func (k *KubernetesClient) deleteKind(resource helm.KubernetesResource, namespace string) error {
205 log.Warn("Deleting Resource", log.Fields{
207 "resource": resource.Name,
210 pluginImpl, err := plugin.GetPluginByKind(resource.GVK.Kind)
212 return pkgerrors.Wrap(err, "Error loading plugin")
215 err = pluginImpl.Delete(resource, namespace, k)
217 return pkgerrors.Wrap(err, "Error deleting "+resource.Name)
223 func (k *KubernetesClient) deleteResources(resources []helm.KubernetesResource, namespace string) error {
224 //TODO: Investigate if deletion should be in a particular order
225 for _, res := range resources {
226 err := k.deleteKind(res, namespace)
228 return pkgerrors.Wrap(err, "Deleting resources")
235 //GetMapper returns the RESTMapper that was created for this client
236 func (k *KubernetesClient) GetMapper() meta.RESTMapper {
240 //GetDynamicClient returns the dynamic client that is needed for
241 //unstructured REST calls to the apiserver
242 func (k *KubernetesClient) GetDynamicClient() dynamic.Interface {
243 return k.dynamicClient
246 // GetStandardClient returns the standard client that can be used to handle
247 // standard kubernetes kinds
248 func (k *KubernetesClient) GetStandardClient() kubernetes.Interface {
252 //GetInstanceID returns the instanceID that is injected into all the
253 //resources created by the plugin
254 func (k *KubernetesClient) GetInstanceID() string {