| // Licensed to the Apache Software Foundation (ASF) under one or more |
| // contributor license agreements. See the NOTICE file distributed with |
| // this work for additional information regarding copyright ownership. |
| // The ASF licenses this file to You 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" |
| "errors" |
| "io/ioutil" |
| "strings" |
| "time" |
| |
| "gopkg.in/yaml.v2" |
| v1 "k8s.io/api/core/v1" |
| |
| "github.com/apache/apisix-ingress-controller/pkg/types" |
| ) |
| |
| const ( |
| // NamespaceAll represents all namespaces. |
| NamespaceAll = "*" |
| // IngressAPISIXLeader is the default election id for the controller |
| // leader election. |
| IngressAPISIXLeader = "ingress-apisix-leader" |
| // IngressClass is the default ingress class name, used for Ingress |
| // object's IngressClassName field in Kubernetes clusters version v1.18.0 |
| // or higher, or the annotation "kubernetes.io/ingress.class" (deprecated). |
| IngressClass = "apisix" |
| |
| // IngressNetworkingV1 represents ingress.networking/v1 |
| IngressNetworkingV1 = "networking/v1" |
| // IngressNetworkingV1beta1 represents ingress.networking/v1beta1 |
| IngressNetworkingV1beta1 = "networking/v1beta1" |
| // IngressExtensionsV1beta1 represents ingress.extensions/v1beta1 |
| // WARNING: ingress.extensions/v1beta1 is deprecated in v1.14+, and will be unavilable |
| // in v1.22. |
| IngressExtensionsV1beta1 = "extensions/v1beta1" |
| // ApisixRouteV1 represents apisixroute.apisix.apache.org/v1 |
| ApisixRouteV1 = "apisix.apache.org/v1" |
| // ApisixRouteV2alpha1 represents apisixroute.apisix.apache.org/v2alpha1 |
| ApisixRouteV2alpha1 = "apisix.apache.org/v2alpha1" |
| |
| _minimalResyncInterval = 30 * time.Second |
| ) |
| |
| // Config contains all config items which are necessary for |
| // apisix-ingress-controller's running. |
| type Config struct { |
| LogLevel string `json:"log_level" yaml:"log_level"` |
| LogOutput string `json:"log_output" yaml:"log_output"` |
| HTTPListen string `json:"http_listen" yaml:"http_listen"` |
| EnableProfiling bool `json:"enable_profiling" yaml:"enable_profiling"` |
| Kubernetes KubernetesConfig `json:"kubernetes" yaml:"kubernetes"` |
| APISIX APISIXConfig `json:"apisix" yaml:"apisix"` |
| } |
| |
| // KubernetesConfig contains all Kubernetes related config items. |
| type KubernetesConfig struct { |
| Kubeconfig string `json:"kubeconfig" yaml:"kubeconfig"` |
| ResyncInterval types.TimeDuration `json:"resync_interval" yaml:"resync_interval"` |
| AppNamespaces []string `json:"app_namespaces" yaml:"app_namespaces"` |
| ElectionID string `json:"election_id" yaml:"election_id"` |
| IngressClass string `json:"ingress_class" yaml:"ingress_class"` |
| IngressVersion string `json:"ingress_version" yaml:"ingress_version"` |
| ApisixRouteVersion string `json:"apisix_route_version" yaml:"apisix_route_version"` |
| } |
| |
| // APISIXConfig contains all APISIX related config items. |
| type APISIXConfig struct { |
| // DefaultClusterName is the name of default cluster. |
| DefaultClusterName string `json:"default_cluster_name"` |
| // DefaultClusterBaseURL is the base url configuration for the default cluster. |
| DefaultClusterBaseURL string `json:"default_cluster_base_url" yaml:"default_cluster_base_url"` |
| // DefaultClusterAdminKey is the admin key for the default cluster. |
| // TODO: Obsolete the plain way to specify admin_key, which is insecure. |
| DefaultClusterAdminKey string `json:"default_cluster_admin_key" yaml:"default_cluster_admin_key"` |
| // BaseURL is same to DefaultClusterBaseURL. |
| // Deprecated: use DefaultClusterBaseURL instead. BaseURL will be removed |
| // once v1.0.0 is released. |
| BaseURL string `json:"base_url" yaml:"base_url"` |
| // AdminKey is same to DefaultClusterAdminKey. |
| // Deprecated: use DefaultClusterAdminKey instead. AdminKey will be removed |
| // once v1.0.0 is released. |
| AdminKey string `json:"admin_key" yaml:"admin_key"` |
| } |
| |
| // NewDefaultConfig creates a Config object which fills all config items with |
| // default value. |
| func NewDefaultConfig() *Config { |
| return &Config{ |
| LogLevel: "warn", |
| LogOutput: "stderr", |
| HTTPListen: ":8080", |
| EnableProfiling: true, |
| Kubernetes: KubernetesConfig{ |
| Kubeconfig: "", // Use in-cluster configurations. |
| ResyncInterval: types.TimeDuration{Duration: 6 * time.Hour}, |
| AppNamespaces: []string{v1.NamespaceAll}, |
| ElectionID: IngressAPISIXLeader, |
| IngressClass: IngressClass, |
| IngressVersion: IngressNetworkingV1, |
| ApisixRouteVersion: ApisixRouteV2alpha1, |
| }, |
| } |
| } |
| |
| // NewConfigFromFile creates a Config object and fills all config items according |
| // to the configuration file. The file can be in JSON/YAML format, which will be |
| // distinguished according to the file suffix. |
| func NewConfigFromFile(filename string) (*Config, error) { |
| cfg := NewDefaultConfig() |
| data, err := ioutil.ReadFile(filename) |
| if err != nil { |
| return nil, err |
| } |
| |
| if strings.HasSuffix(filename, ".yaml") || strings.HasSuffix(filename, ".yml") { |
| err = yaml.Unmarshal(data, cfg) |
| } else { |
| err = json.Unmarshal(data, cfg) |
| } |
| |
| if err != nil { |
| return nil, err |
| } |
| return cfg, nil |
| } |
| |
| // Validate validates whether the Config is right. |
| func (cfg *Config) Validate() error { |
| if cfg.Kubernetes.ResyncInterval.Duration < _minimalResyncInterval { |
| return errors.New("controller resync interval too small") |
| } |
| if cfg.APISIX.DefaultClusterAdminKey == "" { |
| cfg.APISIX.DefaultClusterAdminKey = cfg.APISIX.AdminKey |
| } |
| if cfg.APISIX.DefaultClusterBaseURL == "" { |
| cfg.APISIX.DefaultClusterBaseURL = cfg.APISIX.BaseURL |
| } |
| if cfg.APISIX.DefaultClusterName == "" { |
| cfg.APISIX.DefaultClusterName = "default" |
| } |
| |
| if cfg.APISIX.DefaultClusterBaseURL == "" { |
| return errors.New("apisix base url is required") |
| } |
| switch cfg.Kubernetes.IngressVersion { |
| case IngressNetworkingV1, IngressNetworkingV1beta1, IngressExtensionsV1beta1: |
| break |
| default: |
| return errors.New("unsupported ingress version") |
| } |
| cfg.Kubernetes.AppNamespaces = purifyAppNamespaces(cfg.Kubernetes.AppNamespaces) |
| return nil |
| } |
| |
| func purifyAppNamespaces(namespaces []string) []string { |
| exists := make(map[string]struct{}) |
| var ultimate []string |
| for _, ns := range namespaces { |
| if ns == NamespaceAll { |
| return []string{v1.NamespaceAll} |
| } |
| if _, ok := exists[ns]; !ok { |
| ultimate = append(ultimate, ns) |
| exists[ns] = struct{}{} |
| } |
| } |
| return ultimate |
| } |