Move configuration into config files 54/85854/2
authorKiran Kamineni <kiran.k.kamineni@intel.com>
Thu, 18 Apr 2019 23:38:20 +0000 (16:38 -0700)
committerKiran Kamineni <kiran.k.kamineni@intel.com>
Fri, 19 Apr 2019 22:48:48 +0000 (15:48 -0700)
Move k8splugin configuration into config files
instead of using environment variables.

Issue-ID: MULTICLOUD-579
Change-Id: I7b76d5a14d24f002a8db484097a31fb5e908b6f8
Signed-off-by: Kiran Kamineni <kiran.k.kamineni@intel.com>
deployments/start.sh
src/k8splugin/internal/app/client_test.go
src/k8splugin/internal/app/instance.go
src/k8splugin/internal/app/instance_test.go
src/k8splugin/internal/config/config.go [new file with mode: 0644]
src/k8splugin/internal/config/config_test.go [new file with mode: 0644]
src/k8splugin/internal/db/consul.go
src/k8splugin/internal/db/mongo.go
src/k8splugin/internal/utils.go
src/k8splugin/mock_files/mock_configs/mock_config.json [new file with mode: 0644]
src/k8splugin/mock_files/mock_configs/mock_kube_config [moved from src/k8splugin/mock_files/mock_configs/mock_config with 100% similarity]

index 233e028..f8dc8e7 100755 (executable)
@@ -15,19 +15,25 @@ source /etc/environment
 
 k8s_path="$(git rev-parse --show-toplevel)"
 export GOPATH=$k8s_path
-
-export DATABASE_TYPE=mongo
-export PLUGINS_DIR=$k8s_path/src/k8splugin/plugins
+export GO111MODULE=on
 
 echo "Starting mongo services"
 docker-compose kill
 docker-compose up -d mongo
 export DATABASE_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $(docker ps -aqf "name=mongo"))
-export no_proxy=$no_proxy,$DATABASE_IP
-export NO_PROXY=$NO_PROXY,$DATABASE_IP
+export no_proxy=${no_proxy:-},$DATABASE_IP
+export NO_PROXY=${NO_PROXY:-},$DATABASE_IP
 
 echo "Compiling source code"
 pushd $k8s_path/src/k8splugin/
-make plugins
-env GO111MODULE=on go run cmd/main.go
+cat << EOF > k8sconfig.json
+{
+    "database-address":     "$DATABASE_IP",
+    "database-type": "mongo",
+    "plugin-dir": "$(pwd)/plugins",
+    "kube-config-dir": "$(pwd)/kubeconfigs"
+}
+EOF
+make all
+./k8plugin
 popd
index d023fcf..4cc533e 100644 (file)
@@ -48,7 +48,7 @@ func TestInit(t *testing.T) {
        t.Run("Successfully create Kube Client", func(t *testing.T) {
 
                kubeClient := KubernetesClient{}
-               err := kubeClient.init("../../mock_files/mock_configs/mock_config")
+               err := kubeClient.init("../../mock_files/mock_configs/mock_kube_config")
                if err != nil {
                        t.Fatalf("TestGetKubeClient returned an error (%s)", err)
                }
index 8e9a2b7..8d289d8 100644 (file)
@@ -20,8 +20,8 @@ import (
        "encoding/base64"
        "encoding/json"
        "math/rand"
-       "os"
 
+       "k8splugin/internal/config"
        "k8splugin/internal/db"
        "k8splugin/internal/helm"
        "k8splugin/internal/rb"
@@ -120,7 +120,7 @@ func (v *InstanceClient) Create(i InstanceRequest) (InstanceResponse, error) {
        }
 
        k8sClient := KubernetesClient{}
-       err = k8sClient.init(os.Getenv("KUBE_CONFIG_DIR") + "/" + i.CloudRegion)
+       err = k8sClient.init(config.GetConfiguration().KubeConfigDir + "/" + i.CloudRegion)
        if err != nil {
                return InstanceResponse{}, pkgerrors.Wrap(err, "Getting CloudRegion Information")
        }
@@ -185,7 +185,7 @@ func (v *InstanceClient) Delete(id string) error {
        }
 
        k8sClient := KubernetesClient{}
-       err = k8sClient.init(os.Getenv("KUBE_CONFIG_DIR") + "/" + inst.CloudRegion)
+       err = k8sClient.init(config.GetConfiguration().KubeConfigDir + "/" + inst.CloudRegion)
        if err != nil {
                return pkgerrors.Wrap(err, "Getting CloudRegion Information")
        }
index 3828ed3..ab39dfb 100644 (file)
@@ -15,11 +15,11 @@ package app
 
 import (
        "log"
-       "os"
        "reflect"
        "testing"
 
        utils "k8splugin/internal"
+       "k8splugin/internal/config"
        "k8splugin/internal/db"
        "k8splugin/internal/helm"
        "k8splugin/internal/rb"
@@ -153,14 +153,10 @@ func TestInstanceCreate(t *testing.T) {
                        RBName:      "test-rbdef",
                        RBVersion:   "v1",
                        ProfileName: "profile1",
-                       CloudRegion: "mock_config",
-               }
-
-               err := os.Setenv("KUBE_CONFIG_DIR", "../../mock_files/mock_configs")
-               if err != nil {
-                       t.Fatalf("TestInstanceCreate returned an error (%s)", err)
+                       CloudRegion: "mock_kube_config",
                }
 
+               config.SetConfigValue("KubeConfigDir", "../../mock_files/mock_configs")
                ir, err := ic.Create(input)
                if err != nil {
                        t.Fatalf("TestInstanceCreate returned an error (%s)", err)
@@ -326,7 +322,7 @@ func TestInstanceDelete(t *testing.T) {
                                                        "namespace":"testnamespace",
                                                        "rb-name":"test-rbdef",
                                                        "rb-version":"v1",
-                                                       "cloud-region":"mock_config",
+                                                       "cloud-region":"mock_kube_config",
                                                        "resources": [
                                                                {
                                                                        "GVK": {
@@ -369,7 +365,7 @@ func TestInstanceDelete(t *testing.T) {
                                                        "namespace":"testnamespace",
                                                        "rb-name":"test-rbdef",
                                                        "rb-version":"v1",
-                                                       "cloud-region":"mock_config",
+                                                       "cloud-region":"mock_kube_config",
                                                        "resources": [
                                                                {
                                                                        "GVK": {
diff --git a/src/k8splugin/internal/config/config.go b/src/k8splugin/internal/config/config.go
new file mode 100644 (file)
index 0000000..c3ca905
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * 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 config
+
+import (
+       "encoding/json"
+       "log"
+       "os"
+       "reflect"
+)
+
+// Configuration loads up all the values that are used to configure
+// backend implementations
+type Configuration struct {
+       CAFile            string `json:"ca-file"`
+       ServerCert        string `json:"server-cert"`
+       ServerKey         string `json:"server-key"`
+       Password          string `json:"password"`
+       DatabaseAddress   string `json:"database-address"`
+       DatabaseType      string `json:"database-type"`
+       PluginDir         string `json:"plugin-dir"`
+       EtcdIP            string `json:"etcd-ip"`
+       EtcdCert          string `json:"etcd-cert"`
+       EtcdKey           string `json:"etcd-key"`
+       EtcdCAFile        string `json:"etcd-ca-file"`
+       KubeConfigDir     string `json:"kube-config-dir"`
+       OVNCentralAddress string `json:"ovn-central-address"`
+}
+
+// Config is the structure that stores the configuration
+var gConfig *Configuration
+
+// readConfigFile reads the specified smsConfig file to setup some env variables
+func readConfigFile(file string) (*Configuration, error) {
+       f, err := os.Open(file)
+       if err != nil {
+               return defaultConfiguration(), err
+       }
+       defer f.Close()
+
+       // Setup some defaults here
+       // If the json file has values in it, the defaults will be overwritten
+       conf := defaultConfiguration()
+
+       // Read the configuration from json file
+       decoder := json.NewDecoder(f)
+       err = decoder.Decode(conf)
+       if err != nil {
+               return conf, err
+       }
+
+       return conf, nil
+}
+
+func defaultConfiguration() *Configuration {
+       cwd, err := os.Getwd()
+       if err != nil {
+               log.Println("Error getting cwd. Using .")
+               cwd = "."
+       }
+
+       return &Configuration{
+               CAFile:            "ca.cert",
+               ServerCert:        "server.cert",
+               ServerKey:         "server.key",
+               Password:          "",
+               DatabaseAddress:   "127.0.0.1",
+               DatabaseType:      "mongo",
+               PluginDir:         cwd,
+               EtcdIP:            "127.0.0.1",
+               EtcdCert:          "etcd.cert",
+               EtcdKey:           "etcd.key",
+               EtcdCAFile:        "etcd-ca.cert",
+               KubeConfigDir:     cwd,
+               OVNCentralAddress: "127.0.0.1",
+       }
+}
+
+// GetConfiguration returns the configuration for the app.
+// It will try to load it if it is not already loaded.
+func GetConfiguration() *Configuration {
+       if gConfig == nil {
+               conf, err := readConfigFile("k8sconfig.json")
+               if err != nil {
+                       log.Println("Error loading config file. Using defaults.")
+               }
+               gConfig = conf
+       }
+
+       return gConfig
+}
+
+// SetConfigValue sets a value in the configuration
+// This is mostly used to customize the application and
+// should be used carefully.
+func SetConfigValue(key string, value string) *Configuration {
+       c := GetConfiguration()
+       if value == "" || key == "" {
+               return c
+       }
+
+       v := reflect.ValueOf(c).Elem()
+       if v.Kind() == reflect.Struct {
+               f := v.FieldByName(key)
+               if f.IsValid() {
+                       if f.CanSet() {
+                               if f.Kind() == reflect.String {
+                                       f.SetString(value)
+                               }
+                       }
+               }
+       }
+       return c
+}
diff --git a/src/k8splugin/internal/config/config_test.go b/src/k8splugin/internal/config/config_test.go
new file mode 100644 (file)
index 0000000..ddebfb2
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * 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 config
+
+import (
+       "testing"
+)
+
+func TestReadConfigurationFile(t *testing.T) {
+       t.Run("Non Existent Configuration File", func(t *testing.T) {
+               _, err := readConfigFile("filedoesnotexist.json")
+               if err == nil {
+                       t.Fatal("ReadConfiguationFile: Expected Error, got nil")
+               }
+       })
+
+       t.Run("Read Configuration File", func(t *testing.T) {
+               conf, err := readConfigFile("../../mock_files/mock_configs/mock_config.json")
+               if err != nil {
+                       t.Fatal("ReadConfigurationFile: Error reading file")
+               }
+               if conf.DatabaseType != "mock_db_test" {
+                       t.Fatal("ReadConfigurationFile: Incorrect entry read from file")
+               }
+       })
+}
index 0977cfb..40028c6 100644 (file)
@@ -14,7 +14,7 @@ limitations under the License.
 package db
 
 import (
-       "os"
+       k8sconfig "k8splugin/internal/config"
 
        "github.com/hashicorp/consul/api"
        pkgerrors "github.com/pkg/errors"
@@ -38,7 +38,7 @@ type ConsulStore struct {
 func NewConsulStore(store ConsulKVStore) (Store, error) {
        if store == nil {
                config := api.DefaultConfig()
-               config.Address = os.Getenv("DATABASE_IP") + ":8500"
+               config.Address = k8sconfig.GetConfiguration().DatabaseAddress + ":8500"
 
                consulClient, err := api.NewClient(config)
                if err != nil {
index a9e9d98..d452522 100644 (file)
@@ -19,7 +19,8 @@ package db
 import (
        "golang.org/x/net/context"
        "log"
-       "os"
+
+       "k8splugin/internal/config"
 
        pkgerrors "github.com/pkg/errors"
        "go.mongodb.org/mongo-driver/bson"
@@ -76,7 +77,7 @@ var cursorClose = func(ctx context.Context, cursor *mongo.Cursor) error {
 // If a database with that name exists, it will be returned
 func NewMongoStore(name string, store *mongo.Database) (Store, error) {
        if store == nil {
-               ip := "mongodb://" + os.Getenv("DATABASE_IP") + ":27017"
+               ip := "mongodb://" + config.GetConfiguration().DatabaseAddress + ":27017"
                clientOptions := options.Client()
                clientOptions.ApplyURI(ip)
                mongoClient, err := mongo.NewClient(clientOptions)
index 3b08dd2..7785733 100644 (file)
@@ -15,13 +15,15 @@ package utils
 
 import (
        "io/ioutil"
-       "k8splugin/internal/db"
        "log"
        "os"
        "path/filepath"
        "plugin"
        "strings"
 
+       "k8splugin/internal/config"
+       "k8splugin/internal/db"
+
        pkgerrors "github.com/pkg/errors"
        "k8s.io/apimachinery/pkg/runtime"
        "k8s.io/client-go/kubernetes/scheme"
@@ -65,23 +67,10 @@ var DecodeYAML = func(path string, into runtime.Object) (runtime.Object, error)
        return obj, nil
 }
 
-// CheckEnvVariables checks for required Environment variables
-func CheckEnvVariables() error {
-       envList := []string{"CSAR_DIR", "KUBE_CONFIG_DIR", "PLUGINS_DIR",
-               "DATABASE_TYPE", "DATABASE_IP", "OVN_CENTRAL_ADDRESS"}
-       for _, env := range envList {
-               if _, ok := os.LookupEnv(env); !ok {
-                       return pkgerrors.New("environment variable " + env + " not set")
-               }
-       }
-
-       return nil
-}
-
 // CheckDatabaseConnection checks if the database is up and running and
 // plugin can talk to it
 func CheckDatabaseConnection() error {
-       err := db.CreateDBClient(os.Getenv("DATABASE_TYPE"))
+       err := db.CreateDBClient(config.GetConfiguration().DatabaseType)
        if err != nil {
                return pkgerrors.Cause(err)
        }
@@ -92,10 +81,10 @@ func CheckDatabaseConnection() error {
        }
        // TODO Convert these to configuration files instead of environment variables.
        c := db.EtcdConfig{
-               Endpoint: os.Getenv("ETCD_ENDPOINT_IP"),
-               CertFile: os.Getenv("ETCD_CERT_FILE"),
-               KeyFile:  os.Getenv("ETCD_KEY_FILE"),
-               CAFile:   os.Getenv("ETCD_TRUSTED_CA_FILE"),
+               Endpoint: config.GetConfiguration().EtcdIP,
+               CertFile: config.GetConfiguration().EtcdCert,
+               KeyFile:  config.GetConfiguration().EtcdKey,
+               CAFile:   config.GetConfiguration().EtcdCAFile,
        }
        err = db.NewEtcdClient(nil, c)
        if err != nil {
@@ -106,7 +95,7 @@ func CheckDatabaseConnection() error {
 
 // LoadPlugins loads all the compiled .so plugins
 func LoadPlugins() error {
-       pluginsDir := os.Getenv("PLUGINS_DIR")
+       pluginsDir := config.GetConfiguration().PluginDir
        err := filepath.Walk(pluginsDir,
                func(path string, info os.FileInfo, err error) error {
                        if strings.Contains(path, ".so") {
@@ -127,12 +116,7 @@ func LoadPlugins() error {
 
 // CheckInitialSettings is used to check initial settings required to start api
 func CheckInitialSettings() error {
-       err := CheckEnvVariables()
-       if err != nil {
-               return pkgerrors.Cause(err)
-       }
-
-       err = CheckDatabaseConnection()
+       err := CheckDatabaseConnection()
        if err != nil {
                return pkgerrors.Cause(err)
        }
diff --git a/src/k8splugin/mock_files/mock_configs/mock_config.json b/src/k8splugin/mock_files/mock_configs/mock_config.json
new file mode 100644 (file)
index 0000000..93f2b6b
--- /dev/null
@@ -0,0 +1,5 @@
+{
+    "database-type": "mock_db_test",
+    "database-address": "127.0.0.1",
+    "plugin-dir": "."
+}
\ No newline at end of file