From 835b4c018c5eaf4fbf317155a3a3518f547a9b7e Mon Sep 17 00:00:00 2001 From: dev Date: Tue, 16 Apr 2019 16:19:51 -0400 Subject: [PATCH] init --- .gitignore | 3 + Dockerfile | 3 + Makefile | 6 + cmd/caddy/main.go | 132 +++++++++++++++ go.mod | 31 ++++ go.sum | 123 ++++++++++++++ hack/test/example-deployment.yaml | 24 +++ hack/test/example-ingress.yaml | 13 ++ hack/test/example-service.yaml | 12 ++ internal/controller/action.go | 97 +++++++++++ internal/controller/controller.go | 160 ++++++++++++++++++ internal/controller/status.go | 28 +++ internal/ingress/ingress.go | 3 + internal/store/store.go | 49 ++++++ kubernetes/generated/clusterrole.yaml | 27 +++ kubernetes/generated/clusterrolebinding.yaml | 12 ++ kubernetes/generated/deployment.yaml | 34 ++++ kubernetes/generated/serviceaccount.yaml | 10 ++ .../helm/caddyingresscontroller/Chart.yaml | 4 + .../templates/clusterrole.yaml | 29 ++++ .../templates/clusterrolebinding.yaml | 14 ++ .../templates/deployment.yaml | 40 +++++ .../templates/role.yaml | 30 ++++ .../templates/rolebinding.yaml | 15 ++ .../templates/serviceaccount.yaml | 17 ++ .../helm/caddyingresscontroller/values.yaml | 34 ++++ skaffold.yaml | 12 ++ 27 files changed, 962 insertions(+) create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 Makefile create mode 100644 cmd/caddy/main.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 hack/test/example-deployment.yaml create mode 100644 hack/test/example-ingress.yaml create mode 100644 hack/test/example-service.yaml create mode 100644 internal/controller/action.go create mode 100644 internal/controller/controller.go create mode 100644 internal/controller/status.go create mode 100644 internal/ingress/ingress.go create mode 100644 internal/store/store.go create mode 100644 kubernetes/generated/clusterrole.yaml create mode 100644 kubernetes/generated/clusterrolebinding.yaml create mode 100644 kubernetes/generated/deployment.yaml create mode 100644 kubernetes/generated/serviceaccount.yaml create mode 100644 kubernetes/helm/caddyingresscontroller/Chart.yaml create mode 100644 kubernetes/helm/caddyingresscontroller/templates/clusterrole.yaml create mode 100644 kubernetes/helm/caddyingresscontroller/templates/clusterrolebinding.yaml create mode 100644 kubernetes/helm/caddyingresscontroller/templates/deployment.yaml create mode 100644 kubernetes/helm/caddyingresscontroller/templates/role.yaml create mode 100644 kubernetes/helm/caddyingresscontroller/templates/rolebinding.yaml create mode 100644 kubernetes/helm/caddyingresscontroller/templates/serviceaccount.yaml create mode 100644 kubernetes/helm/caddyingresscontroller/values.yaml create mode 100644 skaffold.yaml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e59b5b5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.DS_Store +bin +vendor \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..36d686a --- /dev/null +++ b/Dockerfile @@ -0,0 +1,3 @@ +FROM scratch +COPY ./bin/ingress-controller . +ENTRYPOINT ["/ingress-controller"] \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..796b59f --- /dev/null +++ b/Makefile @@ -0,0 +1,6 @@ +build: + @mkdir -p bin + CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ./bin/ingress-controller ./cmd/caddy + +publish-docker: + @echo 'TODO :- not implemented' \ No newline at end of file diff --git a/cmd/caddy/main.go b/cmd/caddy/main.go new file mode 100644 index 0000000..30571cc --- /dev/null +++ b/cmd/caddy/main.go @@ -0,0 +1,132 @@ +package main + +import ( + "os" + "time" + + "bitbucket.org/lightcodelabs/ingress/internal/controller" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/apimachinery/pkg/version" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/clientcmd" + "k8s.io/klog" +) + +const ( + // High enough QPS to fit all expected use cases. QPS=0 is not set here, because + // client code is overriding it. + defaultQPS = 1e6 + + // High enough Burst to fit all expected use cases. Burst=0 is not set here, because + // client code is overriding it. + defaultBurst = 1e6 +) + +func main() { + klog.InitFlags(nil) + + // get the namespace to monitor ingress resources for + namespace := os.Getenv("KUBERNETES_NAMESPACE") + if len(namespace) == 0 { + namespace = v1.NamespaceAll + klog.Warning("KUBERNETES_NAMESPACE is unset, will monitor ingresses in all namespaces.") + } + + // TODO :- implement + // parse any flags required to configure the caddy ingress controller + // cfg, err := parseFlags() + // if err != nil { + // klog.Fatal(err) + // } + + // get client to access the kubernetes service api + kubeClient, err := createApiserverClient() + if err != nil { + msg := ` + Error while initiating a connection to the Kubernetes API server. + This could mean the cluster is misconfigured (e.g. it has invalid API server certificates or Service Accounts configuration) + ` + + klog.Fatalf(msg, err) + } + + var resource = "ingresses" + restClient := kubeClient.ExtensionsV1beta1().RESTClient() + + // start ingress controller + c := controller.NewCaddyController(namespace, kubeClient, resource, restClient) + + // TODO :- + // create http server to expose controller health metrics + + klog.Info("Starting the caddy ingress controller") + + // start the ingress controller + stopCh := make(chan struct{}, 1) + defer close(stopCh) + + go c.Run(stopCh) + + select {} +} + +// createApiserverClient creates a new Kubernetes REST client. We assume the +// controller runs inside Kubernetes and use the in-cluster config. +func createApiserverClient() (*kubernetes.Clientset, error) { + cfg, err := clientcmd.BuildConfigFromFlags("", "") + if err != nil { + return nil, err + } + + cfg.QPS = defaultQPS + cfg.Burst = defaultBurst + cfg.ContentType = "application/vnd.kubernetes.protobuf" + + klog.Infof("Creating API client for %s", cfg.Host) + + client, err := kubernetes.NewForConfig(cfg) + if err != nil { + return nil, err + } + + // The client may fail to connect to the API server in the first request + defaultRetry := wait.Backoff{ + Steps: 10, + Duration: 1 * time.Second, + Factor: 1.5, + Jitter: 0.1, + } + + klog.V(2).Info("Trying to discover Kubernetes version") + + var v *version.Info + var retries int + var lastErr error + err = wait.ExponentialBackoff(defaultRetry, func() (bool, error) { + v, err = client.Discovery().ServerVersion() + if err == nil { + return true, nil + } + + lastErr = err + klog.V(2).Infof("Unexpected error discovering Kubernetes version (attempt %v): %v", retries, err) + retries++ + return false, nil + }) + + // err is returned in case of timeout in the exponential backoff (ErrWaitTimeout) + if err != nil { + return nil, lastErr + } + + // this should not happen, warn the user + if retries > 0 { + klog.Warningf("Initial connection to the Kubernetes API server was retried %d times.", retries) + } + + msg := "Running in Kubernetes cluster version v%v.%v (%v) - git (%v) commit %v - platform %v" + klog.Infof(msg, v.Major, v.Minor, v.GitVersion, v.GitTreeState, v.GitCommit, v.Platform) + + return client, nil +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..635cef5 --- /dev/null +++ b/go.mod @@ -0,0 +1,31 @@ +module bitbucket.org/lightcodelabs/ingress + +go 1.12 + +require ( + github.com/gogo/protobuf v1.2.1 // indirect + github.com/golang/protobuf v1.3.1 // indirect + github.com/google/gofuzz v1.0.0 // indirect + github.com/googleapis/gnostic v0.2.0 // indirect + github.com/hashicorp/golang-lru v0.5.1 // indirect + github.com/imdario/mergo v0.3.7 // indirect + github.com/json-iterator/go v1.1.6 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.1 // indirect + github.com/openshift/api v3.9.0+incompatible // indirect + github.com/sirupsen/logrus v1.4.1 // indirect + github.com/spf13/pflag v1.0.3 + github.com/stakater/IngressMonitorController v1.0.62 + golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a // indirect + golang.org/x/net v0.0.0-20190415100556-4a65cf94b679 // indirect + golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a // indirect + golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.2.2 // indirect + k8s.io/api v0.0.0-20190409021203-6e4e0e4f393b + k8s.io/apimachinery v0.0.0-20190404173353-6a84e37a896d + k8s.io/client-go v11.0.1-0.20190409021438-1a26190bd76a+incompatible + k8s.io/klog v0.3.0 + k8s.io/utils v0.0.0-20190308190857-21c4ce38f2a7 // indirect + sigs.k8s.io/yaml v1.1.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..b5b00e2 --- /dev/null +++ b/go.sum @@ -0,0 +1,123 @@ +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= +github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903 h1:LbsanbbD6LieFkXbj9YNNBupiGHJgFeLpO0j0Fza1h8= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf h1:+RRA9JqSOZFfKrOeqr2z77+8R2RKyh8PG66dcu1V0ck= +github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= +github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/googleapis/gnostic v0.2.0 h1:l6N3VoaVzTncYYW+9yOz2LJJammFZGBO13sqgEhpy9g= +github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI= +github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be h1:AHimNtVIpiBjPUhEF5KNCkrUyqTSA5zWUl8sQ2bfGBE= +github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v0.0.0-20190113212917-5533ce8a0da3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/openshift/api v3.9.0+incompatible h1:fJ/KsefYuZAjmrr3+5U9yZIZbTOpVkDDLDLFresAeYs= +github.com/openshift/api v3.9.0+incompatible/go.mod h1:dh9o4Fs58gpFXGSYfnVxGR9PnV53I8TW84pQaJDdGiY= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sirupsen/logrus v1.4.1 h1:GL2rEmy6nsikmW0r8opw9JIRScdMF5hA8cOYLH7In1k= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/stakater/IngressMonitorController v1.0.62 h1:KLC79Qo8YNEcNehPSEDrZ1vEtqPux1fnNnXC4m43Lhk= +github.com/stakater/IngressMonitorController v1.0.62/go.mod h1:LXpqENu96n+xgxMr1GGSRKydCrVaszM21Y8PKTb+JK8= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a h1:Igim7XhdOpBnWPuYJ70XcNpq8q3BCACtVgNfoJxOV7g= +golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190206173232-65e2d4e15006/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190415100556-4a65cf94b679 h1:tzVWzOrXxwAwdSCMrf+mbNrZFxwS0+HLP4m2qxtfdhk= +golang.org/x/net v0.0.0-20190415100556-4a65cf94b679/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a h1:tImsplftrFpALCYumobsd0K86vlAs/eXGFms2txfJfA= +golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e h1:nFYrTHrdrAOpShe27kaFHjsqYSEQ0KWqdWLu3xuZJts= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db h1:6/JqlYfC1CCaLnGceQTI+sDGhC9UBSPAsBqI0Gun6kU= +golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/inf.v0 v0.9.0 h1:3zYtXIO92bvsdS3ggAdA8Gb4Azj0YU+TVY1uGYNFA8o= +gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +k8s.io/api v0.0.0-20190409021203-6e4e0e4f393b h1:aBGgKJUM9Hk/3AE8WaZIApnTxG35kbuQba2w+SXqezo= +k8s.io/api v0.0.0-20190409021203-6e4e0e4f393b/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA= +k8s.io/api v0.0.0-20190415132514-c2f1300cac21 h1:XGJCFakX0XXD6V2frIsafsr96sDU1q6Gcvm2JaFQVyM= +k8s.io/api v0.0.0-20190415132514-c2f1300cac21/go.mod h1:5HMaKNcWJji8AGOBjOZxmFDwRutMItn4MrrXZcFANNo= +k8s.io/api v0.0.0-20190417212520-7b2abe144378 h1:5MbuLXn4OcNwi6vnf6OqDuMvZQQFMG5Bl97Ji6U+v/I= +k8s.io/apimachinery v0.0.0-20190404173353-6a84e37a896d h1:Jmdtdt1ZnoGfWWIIik61Z7nKYgO3J+swQJtPYsP9wHA= +k8s.io/apimachinery v0.0.0-20190404173353-6a84e37a896d/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0= +k8s.io/apimachinery v0.0.0-20190415132420-07d458fe0356 h1:IneIG23feOS594nIIsJVRUHK50ALz/g0/Co/3iML7RI= +k8s.io/apimachinery v0.0.0-20190415132420-07d458fe0356/go.mod h1:jLjiXl596L+wVGdx8dRx6sSNnabzqnI2+nAJqqqyesQ= +k8s.io/client-go v11.0.0+incompatible h1:LBbX2+lOwY9flffWlJM7f1Ct8V2SRNiMRDFeiwnJo9o= +k8s.io/client-go v11.0.0+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s= +k8s.io/client-go v11.0.1-0.20190409021438-1a26190bd76a+incompatible h1:U5Bt+dab9K8qaUmXINrkXO135kA11/i5Kg1RUydgaMQ= +k8s.io/client-go v11.0.1-0.20190409021438-1a26190bd76a+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s= +k8s.io/klog v0.0.0-20190306015804-8e90cee79f82/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v0.3.0 h1:0VPpR+sizsiivjIfIAQH/rl8tan6jvWkS7lU+0di3lE= +k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30 h1:TRb4wNWoBVrH9plmkp2q86FIDppkbrEXdXlxU3a3BMI= +k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc= +k8s.io/utils v0.0.0-20190308190857-21c4ce38f2a7 h1:8r+l4bNWjRlsFYlQJnKJ2p7s1YQPj4XyXiJVqDHRx7c= +k8s.io/utils v0.0.0-20190308190857-21c4ce38f2a7/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3oVhZz7sSzSiPnVZayjIX0= +sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= diff --git a/hack/test/example-deployment.yaml b/hack/test/example-deployment.yaml new file mode 100644 index 0000000..aef7177 --- /dev/null +++ b/hack/test/example-deployment.yaml @@ -0,0 +1,24 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: example + labels: + app: example +spec: + replicas: 1 + selector: + matchLabels: + app: example + template: + metadata: + labels: + app: example + spec: + containers: + - name: httpecho + image: hashicorp/http-echo + args: + - "-listen=:8080" + - "-text=hello world" + ports: + - containerPort: 8080 \ No newline at end of file diff --git a/hack/test/example-ingress.yaml b/hack/test/example-ingress.yaml new file mode 100644 index 0000000..b8d82b3 --- /dev/null +++ b/hack/test/example-ingress.yaml @@ -0,0 +1,13 @@ +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: example +spec: + rules: + - host: hello-world.xyz + http: + paths: + - path: /* + backend: + serviceName: example + servicePort: 8080 \ No newline at end of file diff --git a/hack/test/example-service.yaml b/hack/test/example-service.yaml new file mode 100644 index 0000000..1c72429 --- /dev/null +++ b/hack/test/example-service.yaml @@ -0,0 +1,12 @@ +kind: Service +apiVersion: v1 +metadata: + name: example +spec: + type: NodePort + selector: + app: example + ports: + - protocol: TCP + port: 80 + targetPort: 8080 \ No newline at end of file diff --git a/internal/controller/action.go b/internal/controller/action.go new file mode 100644 index 0000000..a4224f1 --- /dev/null +++ b/internal/controller/action.go @@ -0,0 +1,97 @@ +package controller + +import ( + "fmt" + + "k8s.io/api/extensions/v1beta1" +) + +func (c *CaddyController) onResourceAdded(obj interface{}) { + c.syncQueue.Add(ResourceAddedAction{ + resource: obj, + }) +} + +func (c *CaddyController) onResourceUpdated(old interface{}, new interface{}) { + c.syncQueue.Add(ResourceUpdatedAction{ + resource: new, + oldResource: old, + }) +} + +func (c *CaddyController) onResourceDeleted(obj interface{}) { + c.syncQueue.Add(ResourceDeletedAction{ + resource: obj, + }) +} + +func (c *CaddyController) onSyncStatus(obj interface{}) { + c.syncQueue.Add(SyncStatusAction{}) +} + +// Action is an interface for ingress actions +type Action interface { + handle(c *CaddyController) error +} + +// 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 ResourceAddedAction) handle(c *CaddyController) error { + // 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") + } + + // 1. Parse ingress resource and convert to obj to configure caddy with + // 2. Get current caddy config for rollback purposes + // 3. Update internal caddy config + // 4. Get ingress controller publish address + // 5. call syncIngress for this specific resource + // 6. Add this ingress to resource store + + c.resourceStore.AddIngress(ing) + + // ~~~~ + // when updating caddy config the ingress controller should bypass kube-proxy and get the ip address of + // the pod that the deployment we are proxying to is running on so that we can proxy to that ip address port. + // this is good for session affinity and increases performance (since we don't have to hit dns). + + // example getting an ingress + // ingClient := c.kubeClient.ExtensionsV1beta1().Ingresses(c.namespace) // get a client to update the ingress + // ingClient.UpdateStatus(ing) // pass an ingress with the status.address field updated + // ~~~ + + return nil +} + +func (r ResourceUpdatedAction) handle(c *CaddyController) error { + // find the caddy config related to the oldResource and update it + + fmt.Printf("\nUpdated resource:\n +%v\n\nOld resource: \n %+v\n", r.resource, r.oldResource) + + return nil +} + +func (r ResourceDeletedAction) handle(c *CaddyController) error { + // delete all resources from caddy config that are associated with this resource + // reload caddy config + + fmt.Printf("\nDeleted resource:\n +%v\n", r.resource) + + return nil +} diff --git a/internal/controller/controller.go b/internal/controller/controller.go new file mode 100644 index 0000000..1e0680b --- /dev/null +++ b/internal/controller/controller.go @@ -0,0 +1,160 @@ +package controller + +import ( + "fmt" + "os" + "time" + + "bitbucket.org/lightcodelabs/ingress/internal/store" + "k8s.io/api/extensions/v1beta1" + "k8s.io/apimachinery/pkg/fields" + run "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/cache" + "k8s.io/client-go/util/workqueue" + "k8s.io/klog" +) + +// ResourceMap are resources from where changes are going to be detected +var ResourceMap = map[string]run.Object{ + "ingresses": &v1beta1.Ingress{}, +} + +// how often we should attempt to keep ingress resource's source address in sync +const syncInterval = time.Second * 60 + +// CaddyController represents an caddy ingress controller. +type CaddyController struct { + resourceStore *store.Store + kubeClient *kubernetes.Clientset + namespace string + indexer cache.Indexer + syncQueue workqueue.RateLimitingInterface + statusQueue workqueue.RateLimitingInterface // statusQueue performs ingress status updates every 60 seconds but inserts the work into the sync queue + informer cache.Controller +} + +// NewCaddyController returns an instance of the caddy ingress controller. +func NewCaddyController(namespace string, kubeClient *kubernetes.Clientset, resource string, restClient rest.Interface) *CaddyController { + controller := &CaddyController{ + kubeClient: kubeClient, + namespace: namespace, + syncQueue: workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter()), + statusQueue: workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter()), + } + + ingressListWatcher := cache.NewListWatchFromClient(restClient, resource, namespace, fields.Everything()) + indexer, informer := cache.NewIndexerInformer(ingressListWatcher, ResourceMap[resource], 0, cache.ResourceEventHandlerFuncs{ + AddFunc: controller.onResourceAdded, + UpdateFunc: controller.onResourceUpdated, + DeleteFunc: controller.onResourceDeleted, + }, cache.Indexers{}) + + controller.indexer = indexer + controller.informer = informer + controller.resourceStore = store.NewStore(controller.kubeClient) + + // ======= + // TODO :- get info of the current pod, we'll need the ip address so we can forward requests to this ingress + // controller + + // podInfo, err := k8s.GetPodDetails(kubeClient) + // if err != nil { + // klog.Fatalf("Unexpected error obtaining pod information: %v", err) + // } + // ======= + + // TODO :- attempt to do initial sync with ingresses here + + return controller +} + +// Shutdown stops the caddy controller. +func (c *CaddyController) Shutdown() error { + // TODO :- implement a graceful shutdown for the ingress controller and caddy server + + // shutdown statusQueue + + // shutdown syncQueue + + // shutdownCaddy + + return nil +} + +// handleErrs reports errors received from queue actions. +func (c *CaddyController) handleErr(err error, action interface{}) { + klog.Error(err) +} + +// Run method starts the ingress controller. +func (c *CaddyController) Run(stopCh chan struct{}) { + klog.Info("starting caddy ingress controller") + + // TODO :- start an instance of caddy server + + defer runtime.HandleCrash() + defer c.syncQueue.ShutDown() + + // start the ingress informer where we listen to new / updated ingress resources + go c.informer.Run(stopCh) + + // wait for all involved caches to be synced, before processing items from the queue is started + if !cache.WaitForCacheSync(stopCh, c.informer.HasSynced) { + runtime.HandleError(fmt.Errorf("Timed out waiting for caches to sync")) + return + } + + // start processing events for syncing ingress resources + go wait.Until(c.runWorker, time.Second, stopCh) + + // start ingress status syncher + go wait.Until(c.dispatchSync, syncInterval, stopCh) + + <-stopCh + klog.Info("stopping ingress controller") + + exitCode := 0 + err := c.Shutdown() + if err != nil { + klog.Errorf("could not shutdown ingress controller properly") + exitCode = 1 + } + + os.Exit(exitCode) +} + +// TODO :- copy this for the status updater for ingress controllers +// every 60 seconds attempt to update the statusIP for ingresses +// add into the syncqueue + +// process items in the event queue +func (c *CaddyController) runWorker() { + for c.processNextItem() { + } +} + +// if there is an ingress item in the event queue process it +func (c *CaddyController) processNextItem() bool { + // Wait until there is a new item in the working queue + action, quit := c.syncQueue.Get() + if quit { + return false + } + + // Tell the queue that we are done with processing this key. This unblocks the key for other workers + // This allows safe parallel processing because two ingresses with the same key are never processed in + // parallel. + defer c.syncQueue.Done(action) + + // Invoke the method containing the business logic + err := action.(Action).handle(c) + if err != nil { + c.handleErr(err, action) + } + + return true +} diff --git a/internal/controller/status.go b/internal/controller/status.go new file mode 100644 index 0000000..8256c35 --- /dev/null +++ b/internal/controller/status.go @@ -0,0 +1,28 @@ +package controller + +import ( + "fmt" + + "k8s.io/api/extensions/v1beta1" +) + +// dispatchSync is run every syncInterval duration to sync ingress source address fields. +func (c *CaddyController) dispatchSync() { + c.syncQueue.Add(SyncStatusAction{}) +} + +// SyncStatusAction provides an implementation of the action interface +type SyncStatusAction struct { +} + +func (r SyncStatusAction) handle(c *CaddyController) error { + c.syncStatus(c.resourceStore.Ingresses) + return nil +} + +// syncStatus ensures that the ingress source address points to this ingress controller's IP address. +func (c *CaddyController) syncStatus(ings []*v1beta1.Ingress) { + // TODO :- update source address to ingress controller published address + + fmt.Println("Handle Synching Ingress Source Address") +} diff --git a/internal/ingress/ingress.go b/internal/ingress/ingress.go new file mode 100644 index 0000000..ee5fd06 --- /dev/null +++ b/internal/ingress/ingress.go @@ -0,0 +1,3 @@ +package ingress + +// handle getting ingress information here diff --git a/internal/store/store.go b/internal/store/store.go new file mode 100644 index 0000000..28192a1 --- /dev/null +++ b/internal/store/store.go @@ -0,0 +1,49 @@ +package store + +import ( + "k8s.io/api/extensions/v1beta1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/klog" +) + +// Store represents a collection of ingresses and secrets that we are monitoring. +type Store struct { + Ingresses []*v1beta1.Ingress + Secrets []interface{} // TODO :- should we store the secrets in the ingress object? +} + +// NewStore returns a new store that keeps track of ingresses and secrets. It will attempt to get +// all current ingresses before returning. +func NewStore(kubeClient *kubernetes.Clientset) *Store { + ingresses, err := kubeClient.ExtensionsV1beta1().Ingresses("").List(v1.ListOptions{}) + if err != nil { + klog.Errorf("could not get existing ingresses in cluster") + return &Store{} + } + + s := &Store{ + Ingresses: make([]*v1beta1.Ingress, len(ingresses.Items)-1), + } + + for _, i := range ingresses.Items { + s.Ingresses = append(s.Ingresses, &i) + } + + return s +} + +// AddIngress adds an ingress to the store +func (s *Store) AddIngress(ing *v1beta1.Ingress) { + isUniq := true + + for _, i := range s.Ingresses { + if i.GetUID() == ing.GetUID() { + isUniq = false + } + } + + if isUniq { + s.Ingresses = append(s.Ingresses, ing) + } +} diff --git a/kubernetes/generated/clusterrole.yaml b/kubernetes/generated/clusterrole.yaml new file mode 100644 index 0000000..b090c69 --- /dev/null +++ b/kubernetes/generated/clusterrole.yaml @@ -0,0 +1,27 @@ +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRole +metadata: + name: caddyingresscontroller-role +rules: + - apiGroups: + - "" + - "extensions" + resources: + - ingresses + - routes + verbs: + - list + - get + - update + - patch + - watch + - delete + - apiGroups: + - "" + resources: + - services + - pods + verbs: + - list + - get + - watch diff --git a/kubernetes/generated/clusterrolebinding.yaml b/kubernetes/generated/clusterrolebinding.yaml new file mode 100644 index 0000000..56bcfc2 --- /dev/null +++ b/kubernetes/generated/clusterrolebinding.yaml @@ -0,0 +1,12 @@ +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1beta1 +metadata: + name: caddyingresscontroller-role-binding +roleRef: + kind: ClusterRole + name: caddyingresscontroller-role + apiGroup: rbac.authorization.k8s.io +subjects: +- kind: ServiceAccount + name: caddyingresscontroller + namespace: default \ No newline at end of file diff --git a/kubernetes/generated/deployment.yaml b/kubernetes/generated/deployment.yaml new file mode 100644 index 0000000..95d8846 --- /dev/null +++ b/kubernetes/generated/deployment.yaml @@ -0,0 +1,34 @@ +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: caddyingresscontroller + labels: + app: caddyIngressController + chart: "caddyingresscontroller-v0.1.0" + release: "release-name" + heritage: "Tiller" + version: v0.1.0 + +spec: + replicas: 1 + revisionHistoryLimit: 2 + selector: + matchLabels: + app: caddyIngressController + release: "release-name" + template: + metadata: + labels: + app: caddyIngressController + chart: "caddyingresscontroller-v0.1.0" + release: "release-name" + heritage: "Tiller" + version: v0.1.0 + + spec: + containers: + - env: + image: "caddy/ingresscontroller" + imagePullPolicy: IfNotPresent + name: caddyingresscontroller + serviceAccountName: caddyingresscontroller \ No newline at end of file diff --git a/kubernetes/generated/serviceaccount.yaml b/kubernetes/generated/serviceaccount.yaml new file mode 100644 index 0000000..0a2ffea --- /dev/null +++ b/kubernetes/generated/serviceaccount.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: caddyingresscontroller + labels: + app: caddyIngressController + chart: "caddyingresscontroller-v0.1.0" + release: "release-name" + heritage: "Tiller" + version: v0.1.0 diff --git a/kubernetes/helm/caddyingresscontroller/Chart.yaml b/kubernetes/helm/caddyingresscontroller/Chart.yaml new file mode 100644 index 0000000..32a1591 --- /dev/null +++ b/kubernetes/helm/caddyingresscontroller/Chart.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +description: A helm chart for the Caddy Kubernetes ingress controller +name: caddyingresscontroller +version: v0.1.0 diff --git a/kubernetes/helm/caddyingresscontroller/templates/clusterrole.yaml b/kubernetes/helm/caddyingresscontroller/templates/clusterrole.yaml new file mode 100644 index 0000000..24c4c08 --- /dev/null +++ b/kubernetes/helm/caddyingresscontroller/templates/clusterrole.yaml @@ -0,0 +1,29 @@ +{{- if and ( .Values.caddyingresscontroller.rbac.create ) (eq .Values.caddyingresscontroller.watchNamespace "") }} +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRole +metadata: + name: {{ .Values.name }}-role +rules: + - apiGroups: + - "" + - "extensions" + resources: + - ingresses + - routes + verbs: + - list + - get + - update + - patch + - watch + - delete + - apiGroups: + - "" + resources: + - services + - pods + verbs: + - list + - get + - watch +{{- end }} \ No newline at end of file diff --git a/kubernetes/helm/caddyingresscontroller/templates/clusterrolebinding.yaml b/kubernetes/helm/caddyingresscontroller/templates/clusterrolebinding.yaml new file mode 100644 index 0000000..e7a51a1 --- /dev/null +++ b/kubernetes/helm/caddyingresscontroller/templates/clusterrolebinding.yaml @@ -0,0 +1,14 @@ +{{- if and ( .Values.caddyingresscontroller.rbac.create ) (eq .Values.caddyingresscontroller.watchNamespace "") }} +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1beta1 +metadata: + name: {{ .Values.name }}-role-binding +roleRef: + kind: ClusterRole + name: {{ .Values.name }}-role + apiGroup: rbac.authorization.k8s.io +subjects: +- kind: ServiceAccount + name: {{ .Values.serviceAccountName }} + namespace: {{ .Release.Namespace }} +{{- end }} \ No newline at end of file diff --git a/kubernetes/helm/caddyingresscontroller/templates/deployment.yaml b/kubernetes/helm/caddyingresscontroller/templates/deployment.yaml new file mode 100644 index 0000000..b03ca33 --- /dev/null +++ b/kubernetes/helm/caddyingresscontroller/templates/deployment.yaml @@ -0,0 +1,40 @@ +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: {{ .Values.name }} + labels: + app: {{ .Values.name }} + chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + release: {{ .Release.Name | quote }} + heritage: {{ .Release.Service | quote }} +{{- if .Values.caddyingresscontroller.deployment.labels }} +{{ toYaml .Values.caddyingresscontroller.deployment.labels | indent 4 }} +{{- end }} +spec: + replicas: 1 + revisionHistoryLimit: 2 + selector: + matchLabels: + app: {{ .Values.name }} + release: {{ .Release.Name | quote }} + template: + metadata: + labels: + app: {{ .Values.name }} + chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + release: {{ .Release.Name | quote }} + heritage: {{ .Release.Service | quote }} +{{- if .Values.caddyingresscontroller.deployment.labels }} +{{ toYaml .Values.caddyingresscontroller.deployment.labels | indent 8 }} +{{- end }} + spec: + containers: + - env: + {{- if .Values.caddyingresscontroller.watchNamespace }} + - name: KUBERNETES_NAMESPACE + value: {{ .Values.caddyingresscontroller.watchNamespace | quote }} + {{- end }} + image: "{{ .Values.caddyingresscontroller.image.name }}:{{ .Values.caddyingresscontroller.image.tag }}" + imagePullPolicy: {{ .Values.caddyingresscontroller.image.pullPolicy }} + name: {{ .Values.name }} + serviceAccountName: {{ .Values.serviceAccountName }} \ No newline at end of file diff --git a/kubernetes/helm/caddyingresscontroller/templates/role.yaml b/kubernetes/helm/caddyingresscontroller/templates/role.yaml new file mode 100644 index 0000000..d3742d0 --- /dev/null +++ b/kubernetes/helm/caddyingresscontroller/templates/role.yaml @@ -0,0 +1,30 @@ +{{- if and ( .Values.caddyingresscontroller.rbac.create ) (.Values.caddyingresscontroller.watchNamespace) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ .Values.name }}-role + namespace: {{ .Values.caddyingresscontroller.watchNamespace | quote }} +rules: + - apiGroups: + - "" + - "extensions" + resources: + - ingresses + - routes + verbs: + - list + - get + - update + - patch + - watch + - delete + - apiGroups: + - "" + resources: + - services + - pods + verbs: + - list + - get + - watch +{{- end }} \ No newline at end of file diff --git a/kubernetes/helm/caddyingresscontroller/templates/rolebinding.yaml b/kubernetes/helm/caddyingresscontroller/templates/rolebinding.yaml new file mode 100644 index 0000000..ccec805 --- /dev/null +++ b/kubernetes/helm/caddyingresscontroller/templates/rolebinding.yaml @@ -0,0 +1,15 @@ +{{- if and ( .Values.caddyingresscontroller.rbac.create ) (.Values.caddyingresscontroller.watchNamespace) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ .Values.name }}-role-binding + namespace: {{ .Values.caddyingresscontroller.watchNamespace | quote }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ .Values.name }}-role +subjects: + - kind: ServiceAccount + name: {{ .Values.serviceAccountName }} + namespace: {{ .Release.Namespace | quote }} +{{- end }} \ No newline at end of file diff --git a/kubernetes/helm/caddyingresscontroller/templates/serviceaccount.yaml b/kubernetes/helm/caddyingresscontroller/templates/serviceaccount.yaml new file mode 100644 index 0000000..75d310a --- /dev/null +++ b/kubernetes/helm/caddyingresscontroller/templates/serviceaccount.yaml @@ -0,0 +1,17 @@ +{{- if .Values.caddyingresscontroller.serviceAccount.create }} +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + app: {{ .Values.name }} + chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + release: {{ .Release.Name | quote }} + heritage: {{ .Release.Service | quote }} +{{- if .Values.caddyingresscontroller.serviceAccount.labels }} +{{ toYaml .Values.caddyingresscontroller.serviceAccount.labels | indent 4 }} +{{- end }} +{{- if .Values.caddyingresscontroller.matchLabels }} +{{ toYaml .Values.caddyingresscontroller.matchLabels | indent 4 }} +{{- end }} + name: {{ .Values.serviceAccountName }} +{{- end }} \ No newline at end of file diff --git a/kubernetes/helm/caddyingresscontroller/values.yaml b/kubernetes/helm/caddyingresscontroller/values.yaml new file mode 100644 index 0000000..69c13e1 --- /dev/null +++ b/kubernetes/helm/caddyingresscontroller/values.yaml @@ -0,0 +1,34 @@ +# Default values for caddyingresscontroller. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +kubernetes: + host: https://kubernetes.default + +caddyingresscontroller: + tolerations: {} + watchNamespace: "" + deployment: + labels: + version: "v0.1.0" + config: + labels: + version: "v0.1.0" + rbac: + create: true + # Service account config for the agent pods + serviceAccount: + # Specifies whether a ServiceAccount should be created + create: true + labels: + version: "v0.1.0" + # The name of the ServiceAccount to use. + # If not set and create is true, a name is generated using the fullname template + name: caddyIngressController + image: + name: caddy/ingresscontroller + tag: "v0.1.0" + pullPolicy: IfNotPresent + +name: "caddyingresscontroller" +serviceAccountName: "caddyingresscontroller" \ No newline at end of file diff --git a/skaffold.yaml b/skaffold.yaml new file mode 100644 index 0000000..ed63287 --- /dev/null +++ b/skaffold.yaml @@ -0,0 +1,12 @@ +apiVersion: skaffold/v1beta8 +kind: Config +build: + artifacts: + - image: caddy/ingresscontroller +deploy: + kubectl: + manifests: + - kubernetes/generated/clusterrole.yaml + - kubernetes/generated/clusterrolebinding.yaml + - kubernetes/generated/deployment.yaml + - kubernetes/generated/serviceaccount.yaml