From 0a8c6e97a6530bf266dca84fd6bd5f1de319480e Mon Sep 17 00:00:00 2001 From: smruthi2187 <34555664+smruthi2187@users.noreply.github.com> Date: Mon, 22 Oct 2018 15:24:43 -0700 Subject: [PATCH] Feature flag to enable/disable canary + optional prometheus install (#937) --- canaryconfigmgr/canaryConfigMgr.go | 18 ++++- charts/fission-all/requirements.yaml | 3 +- charts/fission-all/templates/_helpers.tpl | 17 +++++ charts/fission-all/templates/deployment.yaml | 20 +++++- charts/fission-all/values.yaml | 7 ++ charts/fission-core/requirements.yaml | 3 +- charts/fission-core/templates/_helpers.tpl | 15 +++++ charts/fission-core/templates/deployment.yaml | 17 +++++ charts/fission-core/values.yaml | 9 ++- common.go | 4 +- controller/api.go | 6 +- controller/api_test.go | 2 +- controller/canaryConfigApi.go | 26 ++++++++ controller/config.go | 65 +++++++++++++++++++ controller/controller.go | 17 +++-- featureconfig/config.go | 49 ++++++++++++++ featureconfig/types.go | 40 ++++++++++++ fission-bundle/main.go | 10 ++- fission/canaryconfig.go | 2 - hack/release-build.sh | 2 + 20 files changed, 304 insertions(+), 28 deletions(-) create mode 100644 controller/config.go create mode 100644 featureconfig/config.go create mode 100644 featureconfig/types.go diff --git a/canaryconfigmgr/canaryConfigMgr.go b/canaryconfigmgr/canaryConfigMgr.go index 59bc78ce..927e1144 100644 --- a/canaryconfigmgr/canaryConfigMgr.go +++ b/canaryconfigmgr/canaryConfigMgr.go @@ -19,9 +19,11 @@ package canaryconfigmgr import ( "context" "fmt" - log "github.com/sirupsen/logrus" + "os" + "strings" "time" + log "github.com/sirupsen/logrus" k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/fields" @@ -45,7 +47,19 @@ type canaryConfigMgr struct { func MakeCanaryConfigMgr(fissionClient *crd.FissionClient, kubeClient *kubernetes.Clientset, crdClient *rest.RESTClient, prometheusSvc string) (*canaryConfigMgr, error) { if prometheusSvc == "" { - return nil, fmt.Errorf("prometheus service not found, cant create canary config manager") + // handle a case where there is a prometheus server is already installed, try to find the service from env variable + envVars := os.Environ() + for _, envVar := range envVars { + if strings.Contains(envVar, "PROMETHEUS_SERVER_SERVICE_HOST") { + envVarSplit := strings.Split(envVar, "=") + prometheusSvc = envVarSplit[1] + break + } + } + + if prometheusSvc == "" { + return nil, fmt.Errorf("prometheus service not found, cant create canary config manager") + } } configMgr := &canaryConfigMgr{ diff --git a/charts/fission-all/requirements.yaml b/charts/fission-all/requirements.yaml index 693418d9..daa0c3bd 100644 --- a/charts/fission-all/requirements.yaml +++ b/charts/fission-all/requirements.yaml @@ -1,4 +1,5 @@ dependencies: - name: prometheus version: 7.1.0 - repository: https://kubernetes-charts.storage.googleapis.com \ No newline at end of file + repository: https://kubernetes-charts.storage.googleapis.com + condition: prometheusDeploy \ No newline at end of file diff --git a/charts/fission-all/templates/_helpers.tpl b/charts/fission-all/templates/_helpers.tpl index f7877c32..b434342d 100644 --- a/charts/fission-all/templates/_helpers.tpl +++ b/charts/fission-all/templates/_helpers.tpl @@ -14,3 +14,20 @@ We truncate at 24 chars because some Kubernetes name fields are limited to this {{- $name := default .Chart.Name .Values.nameOverride -}} {{- printf "%s-%s" .Release.Name $name | trunc 24 | trimSuffix "-" -}} {{- end -}} + + +{{/* +This is a template with config parameters for optional features in fission. This gets mounted on to the controller pod +as a config map. +To add new features with config parameters, create a yaml block below with the feature name and define a corresponding struct in +controller/config.go +*/}} +{{- define "config" -}} +canary: + enabled: {{ .Values.canaryDeployment.enabled }} + {{- if .Values.prometheusDeploy }} + prometheusSvc: "http://{{ .Release.Name }}-prometheus-server.{{ .Release.Namespace }}" + {{- end }} + {{- printf "\n" -}} +{{- end -}} + diff --git a/charts/fission-all/templates/deployment.yaml b/charts/fission-all/templates/deployment.yaml index f00bb0b6..4e40d958 100644 --- a/charts/fission-all/templates/deployment.yaml +++ b/charts/fission-all/templates/deployment.yaml @@ -103,6 +103,15 @@ metadata: name: fission-builder namespace: {{ .Values.builderNamespace }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: feature-config + namespace: {{ .Release.Namespace }} +data: + "config.yaml": {{ include "config" . | b64enc }} + --- apiVersion: extensions/v1beta1 kind: Deployment @@ -123,7 +132,7 @@ spec: image: "{{ .Values.repository }}/{{ .Values.image }}:{{ .Values.imageTag }}" imagePullPolicy: {{ .Values.pullPolicy }} command: ["/fission-bundle"] - args: ["--controllerPort", "8888", "--prometheusSvc", "http://{{ .Release.Name }}-prometheus-server.{{ .Release.Namespace }}"] + args: ["--controllerPort", "8888"] env: - name: FISSION_FUNCTION_NAMESPACE value: "{{ .Values.functionNamespace }}" @@ -144,8 +153,15 @@ spec: port: 8888 initialDelaySeconds: 35 periodSeconds: 5 + volumeMounts: + - name: config-volume + mountPath: /etc/config/config.yaml + subPath: config.yaml serviceAccount: fission-svc - + volumes: + - name: config-volume + configMap: + name: feature-config --- apiVersion: extensions/v1beta1 kind: Deployment diff --git a/charts/fission-all/values.yaml b/charts/fission-all/values.yaml index 8241294c..ae4fa4af 100644 --- a/charts/fission-all/values.yaml +++ b/charts/fission-all/values.yaml @@ -106,3 +106,10 @@ preUpgradeChecksImage: fission/pre-upgrade-checks ## if there are any pod specialization errors when a function is triggered and this flag is set to true, the error ## summary is returned as part of http response debugEnv: true + +## set this flag to true if prometheus needs to be deployed along with fission +prometheusDeploy: true + +## set this flag to false if you dont need canary deployment feature +canaryDeployment: + enabled: true diff --git a/charts/fission-core/requirements.yaml b/charts/fission-core/requirements.yaml index 693418d9..daa0c3bd 100644 --- a/charts/fission-core/requirements.yaml +++ b/charts/fission-core/requirements.yaml @@ -1,4 +1,5 @@ dependencies: - name: prometheus version: 7.1.0 - repository: https://kubernetes-charts.storage.googleapis.com \ No newline at end of file + repository: https://kubernetes-charts.storage.googleapis.com + condition: prometheusDeploy \ No newline at end of file diff --git a/charts/fission-core/templates/_helpers.tpl b/charts/fission-core/templates/_helpers.tpl index f7877c32..77ecc824 100644 --- a/charts/fission-core/templates/_helpers.tpl +++ b/charts/fission-core/templates/_helpers.tpl @@ -14,3 +14,18 @@ We truncate at 24 chars because some Kubernetes name fields are limited to this {{- $name := default .Chart.Name .Values.nameOverride -}} {{- printf "%s-%s" .Release.Name $name | trunc 24 | trimSuffix "-" -}} {{- end -}} + +{{/* +This is a template with config parameters for optional features in fission. This gets mounted on to the controller pod +as a config map. +To add new features with config parameters, create a yaml block below with the feature name and define a corresponding struct in +controller/config.go +*/}} +{{- define "config" -}} +canary: + enabled: {{ .Values.canaryDeployment.enabled }} + {{- if .Values.prometheusDeploy }} + prometheusSvc: "http://{{ .Release.Name }}-prometheus-server.{{ .Release.Namespace }}" + {{- end }} + {{- printf "\n" -}} +{{- end -}} diff --git a/charts/fission-core/templates/deployment.yaml b/charts/fission-core/templates/deployment.yaml index 3ffe9777..c562dee8 100644 --- a/charts/fission-core/templates/deployment.yaml +++ b/charts/fission-core/templates/deployment.yaml @@ -103,6 +103,15 @@ metadata: name: fission-builder namespace: {{ .Values.builderNamespace }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: feature-config + namespace: {{ .Release.Namespace }} +data: + "config.yaml": {{ include "config" . | b64enc }} + --- apiVersion: extensions/v1beta1 kind: Deployment @@ -144,7 +153,15 @@ spec: port: 8888 initialDelaySeconds: 35 periodSeconds: 5 + volumeMounts: + - name: config-volume + mountPath: /etc/config/config.yaml + subPath: config.yaml serviceAccount: fission-svc + volumes: + - name: config-volume + configMap: + name: feature-config --- apiVersion: extensions/v1beta1 diff --git a/charts/fission-core/values.yaml b/charts/fission-core/values.yaml index 21618f95..4450b6f9 100644 --- a/charts/fission-core/values.yaml +++ b/charts/fission-core/values.yaml @@ -71,4 +71,11 @@ preUpgradeChecksImage: fission/pre-upgrade-checks ## if there are any pod specialization errors when a function is triggered and this flag is set to true, the error ## summary is returned as part of http response -debugEnv: true \ No newline at end of file +debugEnv: true + +## set this flag to true if prometheus needs to be deployed along with fission +prometheusDeploy: true + +## set this flag to false if you dont need canary deployment feature +canaryDeployment: + enabled: true \ No newline at end of file diff --git a/common.go b/common.go index 27381e4d..f3f16375 100644 --- a/common.go +++ b/common.go @@ -28,14 +28,14 @@ import ( "strings" "syscall" + log "github.com/sirupsen/logrus" + "github.com/gorilla/handlers" "github.com/imdario/mergo" "github.com/mholt/archiver" uuid "github.com/satori/go.uuid" apiv1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - "github.com/fission/fission/fission/log" ) func UrlForFunction(name, namespace string) string { diff --git a/controller/api.go b/controller/api.go index a4455b31..020c405a 100644 --- a/controller/api.go +++ b/controller/api.go @@ -32,6 +32,7 @@ import ( "github.com/fission/fission" "github.com/fission/fission/crd" + config "github.com/fission/fission/featureconfig" "github.com/fission/fission/fission/logdb" ) @@ -53,6 +54,7 @@ type ( workflowApiUrl string functionNamespace string useIstio bool + featureConfig *config.FeatureConfig } logDBConfig struct { @@ -62,7 +64,7 @@ type ( } ) -func MakeAPI() (*API, error) { +func MakeAPI(featureConfig *config.FeatureConfig) (*API, error) { api, err := makeCRDBackedAPI() u := os.Getenv("STORAGE_SERVICE_URL") @@ -93,6 +95,8 @@ func MakeAPI() (*API, error) { api.functionNamespace = "fission-function" } + api.featureConfig = featureConfig + return api, err } diff --git a/controller/api_test.go b/controller/api_test.go index 47fd4ffa..c10a0cd4 100644 --- a/controller/api_test.go +++ b/controller/api_test.go @@ -344,7 +344,7 @@ func TestMain(m *testing.M) { return } - go Start(8888, "http://localhost:9090") + go Start(8888, true) time.Sleep(5 * time.Second) g.client = client.MakeClient("http://localhost:8888") diff --git a/controller/canaryConfigApi.go b/controller/canaryConfigApi.go index 943f60b7..bb5ab0b8 100644 --- a/controller/canaryConfigApi.go +++ b/controller/canaryConfigApi.go @@ -25,10 +25,16 @@ import ( log "github.com/sirupsen/logrus" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "github.com/fission/fission" "github.com/fission/fission/crd" ) func (a *API) CanaryConfigApiCreate(w http.ResponseWriter, r *http.Request) { + if !a.featureConfig.CanaryConfig.IsEnabled { + a.respondWithError(w, fission.MakeError(http.StatusBadRequest, "Please enable canary feature while installing fission")) + return + } + body, err := ioutil.ReadAll(r.Body) if err != nil { a.respondWithError(w, err) @@ -60,6 +66,11 @@ func (a *API) CanaryConfigApiCreate(w http.ResponseWriter, r *http.Request) { } func (a *API) CanaryConfigApiGet(w http.ResponseWriter, r *http.Request) { + if !a.featureConfig.CanaryConfig.IsEnabled { + a.respondWithError(w, fission.MakeError(http.StatusBadRequest, "Please enable canary feature while installing fission")) + return + } + vars := mux.Vars(r) name := vars["canaryConfig"] @@ -84,6 +95,11 @@ func (a *API) CanaryConfigApiGet(w http.ResponseWriter, r *http.Request) { } func (a *API) CanaryConfigApiList(w http.ResponseWriter, r *http.Request) { + if !a.featureConfig.CanaryConfig.IsEnabled { + a.respondWithError(w, fission.MakeError(http.StatusBadRequest, "Please enable canary feature while installing fission")) + return + } + ns := a.extractQueryParamFromRequest(r, "namespace") if len(ns) == 0 { ns = metav1.NamespaceDefault @@ -105,6 +121,11 @@ func (a *API) CanaryConfigApiList(w http.ResponseWriter, r *http.Request) { } func (a *API) CanaryConfigApiUpdate(w http.ResponseWriter, r *http.Request) { + if !a.featureConfig.CanaryConfig.IsEnabled { + a.respondWithError(w, fission.MakeError(http.StatusBadRequest, "Please enable canary feature while installing fission")) + return + } + body, err := ioutil.ReadAll(r.Body) if err != nil { a.respondWithError(w, err) @@ -134,6 +155,11 @@ func (a *API) CanaryConfigApiUpdate(w http.ResponseWriter, r *http.Request) { } func (a *API) CanaryConfigApiDelete(w http.ResponseWriter, r *http.Request) { + if !a.featureConfig.CanaryConfig.IsEnabled { + a.respondWithError(w, fission.MakeError(http.StatusBadRequest, "Please enable canary feature while installing fission")) + return + } + vars := mux.Vars(r) name := vars["canaryConfig"] ns := a.extractQueryParamFromRequest(r, "namespace") diff --git a/controller/config.go b/controller/config.go new file mode 100644 index 00000000..214a0b63 --- /dev/null +++ b/controller/config.go @@ -0,0 +1,65 @@ +/* +Copyright 2018 The Fission Authors. + +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 controller + +import ( + "context" + "fmt" + + log "github.com/sirupsen/logrus" + "k8s.io/client-go/kubernetes" + + "github.com/fission/fission/canaryconfigmgr" + "github.com/fission/fission/crd" + config "github.com/fission/fission/featureconfig" +) + +func ConfigCanaryFeature(context context.Context, fissionClient *crd.FissionClient, kubeClient *kubernetes.Clientset, featureConfig *config.FeatureConfig) error { + // start the appropriate controller + if featureConfig.CanaryConfig.IsEnabled { + canaryCfgMgr, err := canaryconfigmgr.MakeCanaryConfigMgr(fissionClient, kubeClient, fissionClient.GetCrdClient(), + featureConfig.CanaryConfig.PrometheusSvc) + if err != nil { + return fmt.Errorf("failed to start canary config manager: %v", err) + } + canaryCfgMgr.Run(context) + log.Printf("Started canary config manager") + } + + return nil +} + +// ConfigureFeatures gets the feature config and configures the features that are enabled +func ConfigureFeatures(context context.Context, unitTestMode bool, fissionClient *crd.FissionClient, kubeClient *kubernetes.Clientset) (*config.FeatureConfig, error) { + // set feature enabled to false if unitTestMode + if unitTestMode { + featureConfig := &config.FeatureConfig{} + return featureConfig, nil + } + + // get the featureConfig from config map mounted onto the file system + featureConfig, err := config.GetFeatureConfig() + if err != nil { + log.Printf("Error getting feature config : %v", err) + return featureConfig, err + } + + // configure respective features + // in the future when new optional features are added, we need to add corresponding feature handlers and invoke them here + err = ConfigCanaryFeature(context, fissionClient, kubeClient, featureConfig) + return featureConfig, err +} diff --git a/controller/controller.go b/controller/controller.go index febd763f..727dcc3e 100644 --- a/controller/controller.go +++ b/controller/controller.go @@ -21,11 +21,10 @@ import ( "log" "github.com/fission/fission" - "github.com/fission/fission/canaryconfigmgr" "github.com/fission/fission/crd" ) -func Start(port int, prometheusSvc string) { +func Start(port int, unitTestFlag bool) { // setup a signal handler for SIGTERM fission.SetupStackTraceHandler() @@ -44,16 +43,16 @@ func Start(port int, prometheusSvc string) { log.Fatalf("Error waiting for CRDs: %v", err) } - // create canary config manager - canaryCfgMgr, err := canaryconfigmgr.MakeCanaryConfigMgr(fc, kc, fc.GetCrdClient(), prometheusSvc) - if err != nil { - log.Fatalf("Failed to start canary config manager: %v", err) - } ctx, cancel := context.WithCancel(context.Background()) + featureConfig, err := ConfigureFeatures(ctx, unitTestFlag, fc, kc) + if err != nil { + log.Printf("Error configuring features : %v. Proceeding without optional features", err.Error()) + // set all features to false for the MakeApi call below + featureConfig.CanaryConfig.IsEnabled = false + } defer cancel() - canaryCfgMgr.Run(ctx) - api, err := MakeAPI() + api, err := MakeAPI(featureConfig) if err != nil { log.Fatalf("Failed to start controller: %v", err) } diff --git a/featureconfig/config.go b/featureconfig/config.go new file mode 100644 index 00000000..e6973209 --- /dev/null +++ b/featureconfig/config.go @@ -0,0 +1,49 @@ +/* +Copyright 2018 The Fission Authors. + +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 featureconfig + +import ( + "encoding/base64" + "fmt" + "io/ioutil" + + "github.com/ghodss/yaml" +) + +// GetFeatureConfig reads the configMap file and unmarshals the config into a feature config struct +func GetFeatureConfig() (*FeatureConfig, error) { + // read the file + b64EncodedContent, err := ioutil.ReadFile(FeatureConfigFile) + if err != nil { + return nil, fmt.Errorf("error reading YAML file %s: %v", FeatureConfigFile, err) + } + + // b64 decode file + yamlContent, err := base64.StdEncoding.DecodeString(string(b64EncodedContent)) + if err != nil { + return nil, fmt.Errorf("error b64 decoding the config : %v", err) + } + + // unmarshal into feature config + featureConfig := &FeatureConfig{} + err = yaml.Unmarshal(yamlContent, featureConfig) + if err != nil { + return nil, fmt.Errorf("error unmarshalling YAML config %v", err) + } + + return featureConfig, err +} diff --git a/featureconfig/types.go b/featureconfig/types.go new file mode 100644 index 00000000..12eecb94 --- /dev/null +++ b/featureconfig/types.go @@ -0,0 +1,40 @@ +/* +Copyright 2018 The Fission Authors. + +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 featureconfig + +const ( + FeatureConfigFile = "/etc/config/config.yaml" +) + +type ( + // config.yaml contains config parameters for optional features + // To add new features with config parameters: + // 1. create a yaml block with feature name in charts/_helpers.tpl + // 2. define a corresponding struct with the feature config for the yaml unmarshal below + // 3. start the appropriate controllers needed for this feature + + FeatureConfig struct { + // In the future more such feature configs can be added here for each optional feature + CanaryConfig CanaryFeatureConfig `json:"canary"` + } + + // specific feature config + CanaryFeatureConfig struct { + IsEnabled bool `json:"enabled"` + PrometheusSvc string `json:"prometheusSvc"` + } +) diff --git a/fission-bundle/main.go b/fission-bundle/main.go index 6d1556c3..8b189c91 100644 --- a/fission-bundle/main.go +++ b/fission-bundle/main.go @@ -19,8 +19,8 @@ import ( "github.com/fission/fission/timer" ) -func runController(port int, prometheusSvc string) { - controller.Start(port, prometheusSvc) +func runController(port int) { + controller.Start(port, false) log.Fatalf("Error: Controller exited.") } @@ -114,7 +114,7 @@ Use it to start one or more of the fission servers: backends. Usage: - fission-bundle --controllerPort= --prometheusSvc= + fission-bundle --controllerPort= fission-bundle --routerPort= [--executorUrl=] fission-bundle --executorPort= [--namespace=] [--fission-namespace=] fission-bundle --kubewatcher [--routerUrl=] @@ -125,7 +125,6 @@ Usage: fission-bundle --version Options: --controllerPort= Port that the controller should listen on. - --prometheusSvc= Service endpoint of prometheus server --routerPort= Port that the router should listen on. --executorPort= Port that the executor should listen on. --storageServicePort= Port that the storage service should listen on. @@ -154,11 +153,10 @@ Options: executorUrl := getStringArgWithDefault(arguments["--executorUrl"], "http://executor.fission") routerUrl := getStringArgWithDefault(arguments["--routerUrl"], "http://router.fission") storageSvcUrl := getStringArgWithDefault(arguments["--storageSvcUrl"], "http://storagesvc.fission") - prometheusSvcUrl := getStringArgWithDefault(arguments["--prometheusSvc"], "") if arguments["--controllerPort"] != nil { port := getPort(arguments["--controllerPort"]) - runController(port, prometheusSvcUrl) + runController(port) } if arguments["--routerPort"] != nil { diff --git a/fission/canaryconfig.go b/fission/canaryconfig.go index a0b068e6..7c3817f1 100644 --- a/fission/canaryconfig.go +++ b/fission/canaryconfig.go @@ -106,8 +106,6 @@ func canaryConfigCreate(c *cli.Context) error { }, } - fmt.Printf("Canary config name : %s, ns : %s, trigger : %s", canaryConfigName, ns, trigger) - _, err = client.CanaryConfigCreate(canaryCfg) util.CheckErr(err, "create canary config") diff --git a/hack/release-build.sh b/hack/release-build.sh index 66fddf0f..8b9d2861 100755 --- a/hack/release-build.sh +++ b/hack/release-build.sh @@ -241,6 +241,8 @@ build_yamls() { helm template ${c} -n ${c}-${version} --set serviceType=NodePort,routerServiceType=NodePort > ${c}-${version}-minikube.yaml # for environments support ELB helm template ${c} -n ${c}-${version} > ${c}-${version}.yaml + # for cases where prometheus installation along with fission is not preferred + helm template ${c} -n ${c}-${version} --set canaryDeployment.prometheusDeploy=false > ${c}-${version}-fission-only.yaml mv *.yaml ${BUILDDIR}/yamls/ done