summaryrefslogtreecommitdiffstats
path: root/src/k8splugin/internal
diff options
context:
space:
mode:
Diffstat (limited to 'src/k8splugin/internal')
-rw-r--r--src/k8splugin/internal/app/client.go146
-rw-r--r--src/k8splugin/internal/app/client_test.go6
-rw-r--r--src/k8splugin/internal/app/instance.go4
-rw-r--r--src/k8splugin/internal/auth/auth.go107
-rw-r--r--src/k8splugin/internal/auth/auth_test.go47
5 files changed, 294 insertions, 16 deletions
diff --git a/src/k8splugin/internal/app/client.go b/src/k8splugin/internal/app/client.go
index fa5fdfd5..cd1ec8a2 100644
--- a/src/k8splugin/internal/app/client.go
+++ b/src/k8splugin/internal/app/client.go
@@ -21,17 +21,30 @@ import (
utils "k8splugin/internal"
pkgerrors "github.com/pkg/errors"
+ "k8s.io/apimachinery/pkg/api/meta"
+ "k8s.io/client-go/discovery"
+ "k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes"
+ "k8s.io/client-go/restmapper"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/helm/pkg/tiller"
)
-type kubernetesClient struct {
- clientSet *kubernetes.Clientset
+// KubernetesResource is the interface that is implemented
+type KubernetesResource interface {
+ Create(yamlFilePath string, namespace string, client *KubernetesClient) (string, error)
+ Delete(kind string, name string, namespace string, client *KubernetesClient) error
+}
+
+type KubernetesClient struct {
+ clientSet *kubernetes.Clientset
+ dynamicClient dynamic.Interface
+ discoverClient *discovery.DiscoveryClient
+ restMapper meta.RESTMapper
}
// GetKubeClient loads the Kubernetes configuation values stored into the local configuration file
-func (k *kubernetesClient) init(configPath string) error {
+func (k *KubernetesClient) init(configPath string) error {
if configPath == "" {
return pkgerrors.New("config not passed and is not found in ~/.kube. ")
}
@@ -46,10 +59,20 @@ func (k *kubernetesClient) init(configPath string) error {
return err
}
+ k.dynamicClient, err = dynamic.NewForConfig(config)
+ if err != nil {
+ return pkgerrors.Wrap(err, "Creating dynamic client")
+ }
+
+ k.discoverClient, err = discovery.NewDiscoveryClientForConfig(config)
+ if err != nil {
+ return pkgerrors.Wrap(err, "Creating discovery client")
+ }
+
return nil
}
-func (k *kubernetesClient) ensureNamespace(namespace string) error {
+func (k *KubernetesClient) ensureNamespace(namespace string) error {
namespacePlugin, ok := utils.LoadedPlugins["namespace"]
if !ok {
return pkgerrors.New("No plugin for namespace resource found")
@@ -82,7 +105,51 @@ func (k *kubernetesClient) ensureNamespace(namespace string) error {
return nil
}
-func (k *kubernetesClient) createKind(kind string, files []string, namespace string) ([]string, error) {
+func (k *KubernetesClient) createGeneric(kind string, files []string, namespace string) ([]string, error) {
+
+ log.Println("Processing items of Kind: " + 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")
+ }
+
+ pluginObject, ok := utils.LoadedPlugins["generic"]
+ if !ok {
+ return nil, pkgerrors.New("No generic plugin found")
+ }
+
+ symbol, err := pluginObject.Lookup("ExportedVariable")
+ if err != nil {
+ return nil, pkgerrors.Wrap(err, "No ExportedVariable symbol found")
+ }
+
+ genericPlugin, ok := symbol.(KubernetesResource)
+ if !ok {
+ return nil, pkgerrors.New("ExportedVariable is not KubernetesResource 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)
+
+ name, err := genericPlugin.Create(f, namespace, k)
+ if err != nil {
+ return nil, pkgerrors.Wrap(err, "Error in generic plugin")
+ }
+
+ resourcesCreated = append(resourcesCreated, name)
+ }
+ return resourcesCreated, nil
+}
+
+func (k *KubernetesClient) createKind(kind string, files []string, namespace string) ([]string, error) {
log.Println("Processing items of Kind: " + kind)
@@ -103,7 +170,8 @@ func (k *kubernetesClient) createKind(kind string, files []string, namespace str
typePlugin, ok := utils.LoadedPlugins[strings.ToLower(kind)]
if !ok {
- return nil, pkgerrors.New("No plugin for kind " + kind + " found")
+ log.Println("No plugin for kind " + kind + " found. Using generic Plugin")
+ return k.createGeneric(kind, files, namespace)
}
symCreateResourceFunc, err := typePlugin.Lookup("Create")
@@ -123,7 +191,7 @@ func (k *kubernetesClient) createKind(kind string, files []string, namespace str
return resourcesCreated, nil
}
-func (k *kubernetesClient) createResources(resMap map[string][]string,
+func (k *KubernetesClient) createResources(resMap map[string][]string,
namespace string) (map[string][]string, error) {
err := k.ensureNamespace(namespace)
@@ -163,17 +231,47 @@ func (k *kubernetesClient) createResources(resMap map[string][]string,
return createdResourceMap, nil
}
-func (k *kubernetesClient) deleteKind(kind string, resources []string, namespace string) error {
+func (k *KubernetesClient) deleteGeneric(kind string, resources []string, namespace string) error {
+ log.Println("Deleting items of Kind: " + kind)
+
+ pluginObject, ok := utils.LoadedPlugins["generic"]
+ if !ok {
+ return pkgerrors.New("No generic plugin found")
+ }
+
+ symbol, err := pluginObject.Lookup("ExportedVariable")
+ if err != nil {
+ return pkgerrors.Wrap(err, "No ExportedVariable symbol found")
+ }
+
+ //Assert that it implements the KubernetesResource
+ genericPlugin, ok := symbol.(KubernetesResource)
+ if !ok {
+ return pkgerrors.New("ExportedVariable is not KubernetesResource type")
+ }
+
+ for _, res := range resources {
+ err = genericPlugin.Delete(kind, res, 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)
typePlugin, ok := utils.LoadedPlugins[strings.ToLower(kind)]
if !ok {
- return pkgerrors.New("No plugin for resource " + kind + " found")
+ log.Println("No plugin for kind " + kind + " found. Using generic Plugin")
+ return k.deleteGeneric(kind, resources, namespace)
}
symDeleteResourceFunc, err := typePlugin.Lookup("Delete")
if err != nil {
- return pkgerrors.Wrap(err, "Error fetching "+kind+" plugin")
+ return pkgerrors.Wrap(err, "Error findinf Delete symbol in plugin")
}
for _, res := range resources {
@@ -187,7 +285,7 @@ func (k *kubernetesClient) deleteKind(kind string, resources []string, namespace
return nil
}
-func (k *kubernetesClient) deleteResources(resMap map[string][]string, namespace string) error {
+func (k *KubernetesClient) deleteResources(resMap map[string][]string, namespace string) error {
//TODO: Investigate if deletion should be in a particular order
for kind, resourceNames := range resMap {
err := k.deleteKind(kind, resourceNames, namespace)
@@ -198,3 +296,29 @@ func (k *kubernetesClient) deleteResources(resMap map[string][]string, namespace
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
+}
+
+//GetDynamicClient returns the dynamic client that is needed for
+//unstructured REST calls to the apiserver
+func (k *KubernetesClient) GetDynamicClient() dynamic.Interface {
+ return k.dynamicClient
+}
diff --git a/src/k8splugin/internal/app/client_test.go b/src/k8splugin/internal/app/client_test.go
index 5999cfa0..b3436431 100644
--- a/src/k8splugin/internal/app/client_test.go
+++ b/src/k8splugin/internal/app/client_test.go
@@ -45,7 +45,7 @@ func LoadMockPlugins(krdLoadedPlugins map[string]*plugin.Plugin) error {
func TestInit(t *testing.T) {
t.Run("Successfully create Kube Client", func(t *testing.T) {
- kubeClient := kubernetesClient{}
+ kubeClient := KubernetesClient{}
err := kubeClient.init("../../mock_files/mock_configs/mock_config")
if err != nil {
t.Fatalf("TestGetKubeClient returned an error (%s)", err)
@@ -71,7 +71,7 @@ func TestCreateResources(t *testing.T) {
t.Fatalf("LoadMockPlugins returned an error (%s)", err)
}
- k8 := kubernetesClient{
+ k8 := KubernetesClient{
clientSet: &kubernetes.Clientset{},
}
@@ -100,7 +100,7 @@ func TestDeleteResources(t *testing.T) {
t.Fatalf("LoadMockPlugins returned an error (%s)", err)
}
- k8 := kubernetesClient{
+ k8 := KubernetesClient{
clientSet: &kubernetes.Clientset{},
}
diff --git a/src/k8splugin/internal/app/instance.go b/src/k8splugin/internal/app/instance.go
index a5b35fef..93305c30 100644
--- a/src/k8splugin/internal/app/instance.go
+++ b/src/k8splugin/internal/app/instance.go
@@ -118,7 +118,7 @@ func (v *InstanceClient) Create(i InstanceRequest) (InstanceResponse, error) {
return InstanceResponse{}, pkgerrors.Wrap(err, "Error resolving helm charts")
}
- k8sClient := kubernetesClient{}
+ k8sClient := KubernetesClient{}
err = k8sClient.init(os.Getenv("KUBE_CONFIG_DIR") + "/" + i.CloudRegion)
if err != nil {
return InstanceResponse{}, pkgerrors.Wrap(err, "Getting CloudRegion Information")
@@ -183,7 +183,7 @@ func (v *InstanceClient) Delete(id string) error {
return pkgerrors.Wrap(err, "Error getting Instance")
}
- k8sClient := kubernetesClient{}
+ k8sClient := KubernetesClient{}
err = k8sClient.init(os.Getenv("KUBE_CONFIG_DIR") + "/" + inst.CloudRegion)
if err != nil {
return pkgerrors.Wrap(err, "Getting CloudRegion Information")
diff --git a/src/k8splugin/internal/auth/auth.go b/src/k8splugin/internal/auth/auth.go
new file mode 100644
index 00000000..3da8f2af
--- /dev/null
+++ b/src/k8splugin/internal/auth/auth.go
@@ -0,0 +1,107 @@
+/*
+ * 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 auth
+
+import (
+ "crypto/tls"
+ "crypto/x509"
+ "encoding/base64"
+ "encoding/pem"
+ "io/ioutil"
+ "log"
+
+ pkgerrors "github.com/pkg/errors"
+)
+
+// GetTLSConfig initializes a tlsConfig using the CA's certificate
+// This config is then used to enable the server for mutual TLS
+func GetTLSConfig(caCertFile string, certFile string, keyFile string) (*tls.Config, error) {
+
+ // Initialize tlsConfig once
+ caCert, err := ioutil.ReadFile(caCertFile)
+
+ if err != nil {
+ return nil, pkgerrors.Wrap(err, "Read CA Cert file")
+ }
+
+ caCertPool := x509.NewCertPool()
+ caCertPool.AppendCertsFromPEM(caCert)
+
+ tlsConfig := &tls.Config{
+ // Change to RequireAndVerify once we have mandatory certs
+ ClientAuth: tls.VerifyClientCertIfGiven,
+ ClientCAs: caCertPool,
+ MinVersion: tls.VersionTLS12,
+ }
+
+ certPEMBlk, err := readPEMBlock(certFile)
+ if err != nil {
+ return nil, pkgerrors.Wrap(err, "Read Cert File")
+ }
+
+ keyPEMBlk, err := readPEMBlock(keyFile)
+ if err != nil {
+ return nil, pkgerrors.Wrap(err, "Read Key File")
+ }
+
+ tlsConfig.Certificates = make([]tls.Certificate, 1)
+ tlsConfig.Certificates[0], err = tls.X509KeyPair(certPEMBlk, keyPEMBlk)
+ if err != nil {
+ return nil, pkgerrors.Wrap(err, "Load x509 cert and key")
+ }
+
+ tlsConfig.BuildNameToCertificate()
+ return tlsConfig, nil
+}
+
+func readPEMBlock(filename string) ([]byte, error) {
+
+ pemData, err := ioutil.ReadFile(filename)
+ if err != nil {
+ return nil, pkgerrors.Wrap(err, "Read PEM File")
+ }
+
+ pemBlock, rest := pem.Decode(pemData)
+ if len(rest) > 0 {
+ log.Println("Pemfile has extra data")
+ }
+
+ if x509.IsEncryptedPEMBlock(pemBlock) {
+ password, err := ioutil.ReadFile(filename + ".pass")
+ if err != nil {
+ return nil, pkgerrors.Wrap(err, "Read Password File")
+ }
+
+ pByte, err := base64.StdEncoding.DecodeString(string(password))
+ if err != nil {
+ return nil, pkgerrors.Wrap(err, "Decode PEM Password")
+ }
+
+ pemData, err = x509.DecryptPEMBlock(pemBlock, pByte)
+ if err != nil {
+ return nil, pkgerrors.Wrap(err, "Decrypt PEM Data")
+ }
+ var newPEMBlock pem.Block
+ newPEMBlock.Type = pemBlock.Type
+ newPEMBlock.Bytes = pemData
+ // Converting back to PEM from DER data you get from
+ // DecryptPEMBlock
+ pemData = pem.EncodeToMemory(&newPEMBlock)
+ }
+
+ return pemData, nil
+}
diff --git a/src/k8splugin/internal/auth/auth_test.go b/src/k8splugin/internal/auth/auth_test.go
new file mode 100644
index 00000000..49494eee
--- /dev/null
+++ b/src/k8splugin/internal/auth/auth_test.go
@@ -0,0 +1,47 @@
+/*
+* Copyright 2018 TechMahindra
+*
+* 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 auth
+
+import (
+ "crypto/tls"
+ "testing"
+)
+
+//Unit test to varify GetTLSconfig func and varify the tls config min version to be 771
+//Assuming cert file name as auth_test.cert
+func TestGetTLSConfig(t *testing.T) {
+ _, err := GetTLSConfig("filedoesnotexist.cert", "filedoesnotexist.cert", "filedoesnotexist.cert")
+ if err == nil {
+ t.Errorf("Test failed, expected error but got none")
+ }
+ tlsConfig, err := GetTLSConfig("../../mock_files/mock_certs/auth_test_certificate.pem",
+ "../../mock_files/mock_certs/auth_test_certificate.pem",
+ "../../mock_files/mock_certs/auth_test_key.pem")
+ if err != nil {
+ t.Fatal("Test Failed as GetTLSConfig returned error: " + err.Error())
+ }
+ expected := tls.VersionTLS12
+ actual := tlsConfig.MinVersion
+ if tlsConfig != nil {
+ if int(actual) != expected {
+ t.Errorf("Test Failed due to version mismatch")
+ }
+ if tlsConfig == nil {
+ t.Errorf("Test Failed due to GetTLSConfig returned nil")
+ }
+ }
+}