blob: 86ba8dc613113da217d9638ed3bd6bc009673963 [file] [log] [blame]
// 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
}