2020-11-09 10:06:14 +01:00

219 lines
5.8 KiB
Go

package controller
import (
"encoding/json"
"fmt"
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
"github.com/caddyserver/caddy/v2/modules/caddytls"
"github.com/caddyserver/ingress/internal/caddy"
config "github.com/caddyserver/ingress/internal/caddy"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"k8s.io/api/networking/v1beta1"
)
// loadConfigMap runs when a config map with caddy config is loaded on app start.
func (c *CaddyController) onLoadConfig(obj interface{}) {
c.syncQueue.Add(LoadConfigAction{
config: obj,
})
}
// onResourceAdded runs when an ingress resource is added to the cluster.
func (c *CaddyController) onResourceAdded(obj interface{}) {
c.syncQueue.Add(ResourceAddedAction{
resource: obj,
})
}
// onResourceUpdated is run when an ingress resource is updated in the cluster.
func (c *CaddyController) onResourceUpdated(old interface{}, new interface{}) {
c.syncQueue.Add(ResourceUpdatedAction{
resource: new,
oldResource: old,
})
}
// onResourceDeleted is run when an ingress resource is deleted from the cluster.
func (c *CaddyController) onResourceDeleted(obj interface{}) {
c.syncQueue.Add(ResourceDeletedAction{
resource: obj,
})
}
// onSyncStatus is run every sync interval to update the source address on ingresses.
func (c *CaddyController) onSyncStatus(obj interface{}) {
c.syncQueue.Add(SyncStatusAction{})
}
// Action is an interface for ingress actions.
type Action interface {
handle(c *CaddyController) error
}
// LoadConfigAction provides an implementation of the action interface.
type LoadConfigAction struct {
config interface{}
}
// ResourceAddedAction provides an implementation of the action interface.
type ResourceAddedAction struct {
resource interface{}
}
// ResourceUpdatedAction provides an implementation of the action interface.
type ResourceUpdatedAction struct {
resource interface{}
oldResource interface{}
}
// ResourceDeletedAction provides an implementation of the action interface.
type ResourceDeletedAction struct {
resource interface{}
}
func (r LoadConfigAction) handle(c *CaddyController) error {
logrus.Info("Config file detected, updating Caddy config...")
c.resourceStore.CaddyConfig = r.config.(*config.Config)
err := regenerateConfig(c)
if err != nil {
return err
}
return nil
}
func (r ResourceAddedAction) handle(c *CaddyController) error {
logrus.Info("New ingress resource detected, updating Caddy config...")
// configure caddy to handle this resource
ing, ok := r.resource.(*v1beta1.Ingress)
if !ok {
return fmt.Errorf("ResourceAddedAction: incoming resource is not of type ingress")
}
// add this ingress to the internal store
c.resourceStore.AddIngress(ing)
err := regenerateConfig(c)
if err != nil {
return err
}
// ensure that ingress source is updated to point to this ingress controller's ip
err = c.syncStatus([]*v1beta1.Ingress{ing})
if err != nil {
return errors.Wrapf(err, "syncing ingress source address name: %v", ing.GetName())
}
logrus.Info("Caddy reloaded successfully.")
return nil
}
func (r ResourceUpdatedAction) handle(c *CaddyController) error {
logrus.Info("Ingress resource update detected, updating Caddy config...")
// update caddy config regarding this ingress
ing, ok := r.resource.(*v1beta1.Ingress)
if !ok {
return fmt.Errorf("ResourceAddedAction: incoming resource is not of type ingress")
}
// add or update this ingress in the internal store
c.resourceStore.AddIngress(ing)
err := regenerateConfig(c)
if err != nil {
return err
}
logrus.Info("Caddy reloaded successfully.")
return nil
}
func (r ResourceDeletedAction) handle(c *CaddyController) error {
logrus.Info("Ingress resource deletion detected, updating Caddy config...")
// delete all resources from caddy config that are associated with this resource
// reload caddy config
ing, ok := r.resource.(*v1beta1.Ingress)
if !ok {
return fmt.Errorf("ResourceAddedAction: incoming resource is not of type ingress")
}
// add this ingress to the internal store
c.resourceStore.PluckIngress(ing)
err := regenerateConfig(c)
if err != nil {
return err
}
logrus.Info("Caddy reloaded successfully.")
return nil
}
// regenerateConfig regenerate caddy config with updated resources.
func regenerateConfig(c *CaddyController) error {
logrus.Info("Updating caddy config")
var cfg *config.Config
var cfgFile *config.Config = nil
var err error
if c.usingConfigMap {
cfgFile, err = loadCaddyConfigFile("/etc/caddy/config.json")
if err != nil {
logrus.Warn("Unable to load config file: %v", err)
}
}
cfg = config.NewConfig(c.podInfo.Namespace, cfgFile)
tlsApp := cfg.Apps["tls"].(*caddytls.TLS)
httpApp := cfg.Apps["http"].(*caddyhttp.App)
if c.resourceStore.ConfigMap != nil {
err := setConfigMapOptions(c, cfg)
if err != nil {
return errors.Wrap(err, "caddy config reload")
}
}
// if certs are defined on an ingress resource we need to handle them.
tlsCfg, err := c.HandleOwnCertManagement(c.resourceStore.Ingresses)
if err != nil {
return errors.Wrap(err, "caddy config reload")
}
// after TLS secrets are synched we should load them in the cert pool
// and skip auto https for hosts with certs provided
if tlsCfg != nil {
tlsApp.CertificatesRaw["load_folders"] = tlsCfg["load_folders"].(json.RawMessage)
if hosts, ok := tlsCfg["hosts"].([]string); ok {
httpApp.Servers["ingress_server"].AutoHTTPS.Skip = hosts
}
}
if !c.usingConfigMap {
serverRoutes, err := caddy.ConvertToCaddyConfig(c.resourceStore.Ingresses)
if err != nil {
return errors.Wrap(err, "converting ingress resources to caddy config")
}
// set the http server routes
httpApp.Servers["ingress_server"].Routes = serverRoutes
}
// reload caddy with new config
err = c.reloadCaddy(cfg)
if err != nil {
return errors.Wrap(err, "caddy config reload")
}
return nil
}