├── integration ├── .gitignore ├── tests │ ├── browser-cache-ttl │ │ ├── expected.txt │ │ ├── verify.sh │ │ ├── Makefile │ │ ├── spec.yaml │ │ ├── after.sh │ │ └── before.sh │ ├── cache-level │ │ ├── expected.txt │ │ ├── verify.sh │ │ ├── Makefile │ │ ├── spec.yaml │ │ ├── after.sh │ │ └── before.sh │ ├── min-tls-version │ │ ├── expected.txt │ │ ├── verify.sh │ │ ├── Makefile │ │ ├── spec.yaml │ │ ├── after.sh │ │ └── before.sh │ ├── always-use-https-off │ │ ├── expected.txt │ │ ├── verify.sh │ │ ├── Makefile │ │ ├── spec.yaml │ │ ├── after.sh │ │ └── before.sh │ ├── always-use-https-on │ │ ├── expected.txt │ │ ├── verify.sh │ │ ├── Makefile │ │ ├── spec.yaml │ │ ├── after.sh │ │ └── before.sh │ ├── minify │ │ ├── expected.txt │ │ ├── verify.sh │ │ ├── Makefile │ │ ├── spec.yaml │ │ ├── after.sh │ │ └── before.sh │ ├── mobile-redirect │ │ ├── verify.sh │ │ ├── expected.txt │ │ ├── Makefile │ │ ├── after.sh │ │ ├── spec.yaml │ │ └── before.sh │ └── common.mk └── Makefile ├── config ├── .gitignore └── crds │ ├── v1beta1 │ ├── crds.kubeflare.io_workerroutes.yaml │ ├── crds.kubeflare.io_webapplicationfirewallrules.yaml │ ├── crds.kubeflare.io_apitokens.yaml │ ├── crds.kubeflare.io_dnsrecords.yaml │ ├── crds.kubeflare.io_pagerules.yaml │ └── crds.kubeflare.io_accessapplications.yaml │ └── v1 │ ├── crds.kubeflare.io_workerroutes.yaml │ ├── crds.kubeflare.io_webapplicationfirewallrules.yaml │ ├── crds.kubeflare.io_apitokens.yaml │ ├── crds.kubeflare.io_dnsrecords.yaml │ ├── crds.kubeflare.io_pagerules.yaml │ └── crds.kubeflare.io_accessapplications.yaml ├── docs ├── docs │ ├── install │ │ ├── helm.md │ │ └── kubectl.md │ ├── api │ │ ├── apitoken.md │ │ ├── wafrules.md │ │ ├── dnsrecord.md │ │ ├── workerroutes.md │ │ └── pagerules.md │ ├── index.md │ └── getting-started │ │ ├── api-support.md │ │ ├── tutorial.md │ │ └── importing.md ├── requirements.txt └── mkdocs.yml ├── PROJECT ├── pkg ├── version │ ├── build.go │ └── version.go ├── internal │ └── constants.go ├── cli │ ├── kubeflarecli │ │ ├── version.go │ │ ├── root.go │ │ ├── manager.go │ │ └── import.go │ └── integrationcli │ │ ├── root.go │ │ └── run.go ├── apis │ ├── crds │ │ ├── group.go │ │ └── v1alpha1 │ │ │ ├── doc.go │ │ │ ├── register.go │ │ │ ├── dnsrecord_types.go │ │ │ ├── workerroute_types.go │ │ │ ├── webapplicationfirewallrule_types.go │ │ │ ├── apitoken_types.go │ │ │ ├── pagerule_types.go │ │ │ ├── accessapplication_types.go │ │ │ └── zone_types.go │ ├── addtoscheme_crds_v1alpha1.go │ └── apis.go ├── client │ └── kubeflareclientset │ │ ├── fake │ │ ├── doc.go │ │ ├── register.go │ │ └── clientset_generated.go │ │ ├── doc.go │ │ ├── typed │ │ └── crds │ │ │ └── v1alpha1 │ │ │ ├── fake │ │ │ ├── doc.go │ │ │ ├── fake_crds_client.go │ │ │ ├── fake_zone.go │ │ │ └── fake_apitoken.go │ │ │ ├── doc.go │ │ │ ├── generated_expansion.go │ │ │ └── crds_client.go │ │ ├── scheme │ │ ├── doc.go │ │ └── register.go │ │ └── clientset.go ├── controller │ ├── shared │ │ ├── strings.go │ │ ├── zone.go │ │ └── cloudflare.go │ ├── controller.go │ ├── webapplicationfirewallrule │ │ ├── webapplicationfirewallrule.go │ │ └── webapplicationfirewallrule_controller.go │ ├── accessapplication │ │ ├── cors_headers.go │ │ ├── cors_headers_test.go │ │ ├── access_policies.go │ │ └── accessapplication_controller.go │ ├── zone │ │ └── settings_test.go │ ├── dnsrecord │ │ ├── dnsrecord_controller.go │ │ └── dns_records.go │ └── pagerule │ │ └── pagerule_controller.go ├── logger │ └── logger.go ├── cloudflare │ ├── dns │ │ └── import.go │ ├── workerroute │ │ └── import.go │ └── pagerules │ │ └── import.go └── webhook │ └── webhook.go ├── cmd ├── kubeflare │ └── main.go └── integration │ └── main.go ├── Dockerfile.manager ├── deploy └── Dockerfile.manager ├── .gitignore ├── .github └── workflows │ ├── build.yaml │ └── release.yaml ├── hack └── boilerplate.go.txt ├── CONTRIBUTING.md ├── .goreleaser.yaml ├── go.mod ├── README.md └── Makefile /integration/.gitignore: -------------------------------------------------------------------------------- 1 | out/ 2 | -------------------------------------------------------------------------------- /config/.gitignore: -------------------------------------------------------------------------------- 1 | samples 2 | webhook 3 | -------------------------------------------------------------------------------- /integration/tests/browser-cache-ttl/expected.txt: -------------------------------------------------------------------------------- 1 | 86400 2 | -------------------------------------------------------------------------------- /integration/tests/cache-level/expected.txt: -------------------------------------------------------------------------------- 1 | "basic" 2 | -------------------------------------------------------------------------------- /integration/tests/min-tls-version/expected.txt: -------------------------------------------------------------------------------- 1 | "1.2" 2 | -------------------------------------------------------------------------------- /integration/tests/always-use-https-off/expected.txt: -------------------------------------------------------------------------------- 1 | "off" 2 | -------------------------------------------------------------------------------- /integration/tests/always-use-https-on/expected.txt: -------------------------------------------------------------------------------- 1 | "on" 2 | -------------------------------------------------------------------------------- /docs/docs/install/helm.md: -------------------------------------------------------------------------------- 1 | # Install Kubeflare using Helm 2 | 3 | -------------------------------------------------------------------------------- /PROJECT: -------------------------------------------------------------------------------- 1 | version: "1" 2 | domain: kubeflare.io 3 | repo: github.com/replicatedhq/kubeflare 4 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | mkdocs==1.1.2 2 | mkdocs-material==6.2.5 3 | mkdocs-material-extensions==1.0.1 4 | -------------------------------------------------------------------------------- /integration/tests/minify/expected.txt: -------------------------------------------------------------------------------- 1 | { 2 | "js": "on", 3 | "css": "on", 4 | "html": "off" 5 | } 6 | -------------------------------------------------------------------------------- /integration/tests/minify/verify.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cat ./out/result.json | jq '.result.value' > ./out/actual.txt -------------------------------------------------------------------------------- /integration/tests/cache-level/verify.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cat ./out/result.json | jq '.result.value' > ./out/actual.txt -------------------------------------------------------------------------------- /integration/tests/browser-cache-ttl/verify.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cat ./out/result.json | jq '.result.value' > ./out/actual.txt -------------------------------------------------------------------------------- /integration/tests/min-tls-version/verify.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cat ./out/result.json | jq '.result.value' > ./out/actual.txt -------------------------------------------------------------------------------- /integration/tests/mobile-redirect/verify.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cat ./out/result.json | jq '.result.value' > ./out/actual.txt -------------------------------------------------------------------------------- /integration/tests/always-use-https-off/verify.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cat ./out/result.json | jq '.result.value' > ./out/actual.txt -------------------------------------------------------------------------------- /integration/tests/always-use-https-on/verify.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cat ./out/result.json | jq '.result.value' > ./out/actual.txt -------------------------------------------------------------------------------- /integration/tests/minify/Makefile: -------------------------------------------------------------------------------- 1 | include ../common.mk 2 | 3 | 4 | .PHONY: run 5 | run: commonrun 6 | diff -B ./expected.txt ./out/actual.txt -------------------------------------------------------------------------------- /integration/tests/mobile-redirect/expected.txt: -------------------------------------------------------------------------------- 1 | { 2 | "status": "on", 3 | "mobile_subdomain": "mobile", 4 | "strip_uri": true 5 | } 6 | -------------------------------------------------------------------------------- /integration/tests/cache-level/Makefile: -------------------------------------------------------------------------------- 1 | include ../common.mk 2 | 3 | 4 | .PHONY: run 5 | run: commonrun 6 | diff -B ./expected.txt ./out/actual.txt -------------------------------------------------------------------------------- /integration/tests/always-use-https-off/Makefile: -------------------------------------------------------------------------------- 1 | include ../common.mk 2 | 3 | .PHONY: run 4 | run: commonrun 5 | diff -B ./expected.txt ./out/actual.txt -------------------------------------------------------------------------------- /integration/tests/min-tls-version/Makefile: -------------------------------------------------------------------------------- 1 | include ../common.mk 2 | 3 | 4 | .PHONY: run 5 | run: commonrun 6 | diff -B ./expected.txt ./out/actual.txt -------------------------------------------------------------------------------- /integration/tests/mobile-redirect/Makefile: -------------------------------------------------------------------------------- 1 | include ../common.mk 2 | 3 | 4 | .PHONY: run 5 | run: commonrun 6 | diff -B ./expected.txt ./out/actual.txt -------------------------------------------------------------------------------- /integration/tests/always-use-https-on/Makefile: -------------------------------------------------------------------------------- 1 | include ../common.mk 2 | 3 | 4 | .PHONY: run 5 | run: commonrun 6 | diff -B ./expected.txt ./out/actual.txt -------------------------------------------------------------------------------- /integration/tests/browser-cache-ttl/Makefile: -------------------------------------------------------------------------------- 1 | include ../common.mk 2 | 3 | 4 | .PHONY: run 5 | run: commonrun 6 | diff -B ./expected.txt ./out/actual.txt -------------------------------------------------------------------------------- /pkg/version/build.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | // NOTE: these variables are injected at build time 4 | 5 | var ( 6 | version, gitSHA, buildTime string 7 | ) 8 | -------------------------------------------------------------------------------- /cmd/kubeflare/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/replicatedhq/kubeflare/pkg/cli/kubeflarecli" 4 | 5 | func main() { 6 | kubeflarecli.InitAndExecute() 7 | } 8 | -------------------------------------------------------------------------------- /cmd/integration/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/replicatedhq/kubeflare/pkg/cli/integrationcli" 4 | 5 | func main() { 6 | integrationcli.InitAndExecute() 7 | } 8 | -------------------------------------------------------------------------------- /integration/tests/cache-level/spec.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: crds.kubeflare.io/v1alpha1 2 | kind: Zone 3 | metadata: 4 | name: always-use-https 5 | spec: 6 | apiToken: UNUSED 7 | settings: 8 | cacheLevel: basic -------------------------------------------------------------------------------- /integration/tests/min-tls-version/spec.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: crds.kubeflare.io/v1alpha1 2 | kind: Zone 3 | metadata: 4 | name: always-use-https 5 | spec: 6 | apiToken: UNUSED 7 | settings: 8 | minTLSVersion: "1.2" -------------------------------------------------------------------------------- /integration/tests/always-use-https-off/spec.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: crds.kubeflare.io/v1alpha1 2 | kind: Zone 3 | metadata: 4 | name: always-use-https 5 | spec: 6 | apiToken: UNUSED 7 | settings: 8 | alwaysUseHttps: false -------------------------------------------------------------------------------- /integration/tests/always-use-https-on/spec.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: crds.kubeflare.io/v1alpha1 2 | kind: Zone 3 | metadata: 4 | name: always-use-https 5 | spec: 6 | apiToken: UNUSED 7 | settings: 8 | alwaysUseHttps: true -------------------------------------------------------------------------------- /integration/tests/browser-cache-ttl/spec.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: crds.kubeflare.io/v1alpha1 2 | kind: Zone 3 | metadata: 4 | name: always-use-https 5 | spec: 6 | apiToken: UNUSED 7 | settings: 8 | browserCacheTTL: 86400 -------------------------------------------------------------------------------- /integration/tests/minify/spec.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: crds.kubeflare.io/v1alpha1 2 | kind: Zone 3 | metadata: 4 | name: always-use-https 5 | spec: 6 | apiToken: UNUSED 7 | settings: 8 | minify: 9 | css: on 10 | js: on 11 | html: off -------------------------------------------------------------------------------- /integration/tests/minify/after.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | curl -X GET "https://api.cloudflare.com/client/v4/zones/${CF_ZONE_ID}/settings/minify" \ 4 | -H "X-Auth-Email: ${CF_API_EMAIL}" \ 5 | -H "X-Auth-Key: ${CF_API_KEY}" \ 6 | -H "Content-Type: application/json" -------------------------------------------------------------------------------- /integration/tests/cache-level/after.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | curl -X GET "https://api.cloudflare.com/client/v4/zones/${CF_ZONE_ID}/settings/cache_level" \ 4 | -H "X-Auth-Email: ${CF_API_EMAIL}" \ 5 | -H "X-Auth-Key: ${CF_API_KEY}" \ 6 | -H "Content-Type: application/json" -------------------------------------------------------------------------------- /pkg/internal/constants.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | const ( 4 | DeleteCFResourceFinalizer = "finalizers.kubeflare.io/delete-cf-resource" 5 | ProtectAPITokenFinalizer = "finalizers.kubeflare.io/protect-apitoken" 6 | ImportedIDAnnotation = "crds.kubeflare.io/imported-id" 7 | ) 8 | -------------------------------------------------------------------------------- /integration/tests/min-tls-version/after.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | curl -X GET "https://api.cloudflare.com/client/v4/zones/${CF_ZONE_ID}/settings/min_tls_version" \ 4 | -H "X-Auth-Email: ${CF_API_EMAIL}" \ 5 | -H "X-Auth-Key: ${CF_API_KEY}" \ 6 | -H "Content-Type: application/json" -------------------------------------------------------------------------------- /integration/tests/mobile-redirect/after.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | curl -X GET "https://api.cloudflare.com/client/v4/zones/${CF_ZONE_ID}/settings/mobile_redirect" \ 4 | -H "X-Auth-Email: ${CF_API_EMAIL}" \ 5 | -H "X-Auth-Key: ${CF_API_KEY}" \ 6 | -H "Content-Type: application/json" -------------------------------------------------------------------------------- /integration/tests/always-use-https-on/after.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | curl -X GET "https://api.cloudflare.com/client/v4/zones/${CF_ZONE_ID}/settings/always_use_https" \ 4 | -H "X-Auth-Email: ${CF_API_EMAIL}" \ 5 | -H "X-Auth-Key: ${CF_API_KEY}" \ 6 | -H "Content-Type: application/json" -------------------------------------------------------------------------------- /integration/tests/browser-cache-ttl/after.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | curl -X GET "https://api.cloudflare.com/client/v4/zones/${CF_ZONE_ID}/settings/browser_cache_ttl" \ 4 | -H "X-Auth-Email: ${CF_API_EMAIL}" \ 5 | -H "X-Auth-Key: ${CF_API_KEY}" \ 6 | -H "Content-Type: application/json" -------------------------------------------------------------------------------- /integration/tests/always-use-https-off/after.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | curl -X GET "https://api.cloudflare.com/client/v4/zones/${CF_ZONE_ID}/settings/always_use_https" \ 4 | -H "X-Auth-Email: ${CF_API_EMAIL}" \ 5 | -H "X-Auth-Key: ${CF_API_KEY}" \ 6 | -H "Content-Type: application/json" -------------------------------------------------------------------------------- /integration/tests/mobile-redirect/spec.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: crds.kubeflare.io/v1alpha1 2 | kind: Zone 3 | metadata: 4 | name: always-use-https 5 | spec: 6 | apiToken: UNUSED 7 | settings: 8 | mobileRedirect: 9 | status: true 10 | mobileSubdomain: "mobile" 11 | stripURI: true -------------------------------------------------------------------------------- /integration/Makefile: -------------------------------------------------------------------------------- 1 | SHELL := /bin/bash 2 | 3 | all: run 4 | 5 | .PHONY: run 6 | run: 7 | make -C tests/always-use-https-on run 8 | make -C tests/always-use-https-off run 9 | make -C tests/browser-cache-ttl run 10 | make -C tests/cache-level run 11 | make -C tests/minify run 12 | make -C tests/mobile-redirect run -------------------------------------------------------------------------------- /integration/tests/cache-level/before.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | curl -X PATCH "https://api.cloudflare.com/client/v4/zones/${CF_ZONE_ID}/settings/cache_level" \ 4 | -H "X-Auth-Email: ${CF_API_EMAIL}" \ 5 | -H "X-Auth-Key: ${CF_API_KEY}" \ 6 | -H "Content-Type: application/json" \ 7 | --data '{"value":"aggressive"}' 8 | -------------------------------------------------------------------------------- /integration/tests/min-tls-version/before.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | curl -X PATCH "https://api.cloudflare.com/client/v4/zones/${CF_ZONE_ID}/settings/min_tls_version" \ 4 | -H "X-Auth-Email: ${CF_API_EMAIL}" \ 5 | -H "X-Auth-Key: ${CF_API_KEY}" \ 6 | -H "Content-Type: application/json" \ 7 | --data '{"value":"1.0"}' 8 | -------------------------------------------------------------------------------- /integration/tests/always-use-https-off/before.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | curl -X PATCH "https://api.cloudflare.com/client/v4/zones/${CF_ZONE_ID}/settings/always_use_https" \ 4 | -H "X-Auth-Email: ${CF_API_EMAIL}" \ 5 | -H "X-Auth-Key: ${CF_API_KEY}" \ 6 | -H "Content-Type: application/json" \ 7 | --data '{"value":"on"}' 8 | -------------------------------------------------------------------------------- /integration/tests/always-use-https-on/before.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | curl -X PATCH "https://api.cloudflare.com/client/v4/zones/${CF_ZONE_ID}/settings/always_use_https" \ 4 | -H "X-Auth-Email: ${CF_API_EMAIL}" \ 5 | -H "X-Auth-Key: ${CF_API_KEY}" \ 6 | -H "Content-Type: application/json" \ 7 | --data '{"value":"off"}' 8 | -------------------------------------------------------------------------------- /integration/tests/browser-cache-ttl/before.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | curl -X PATCH "https://api.cloudflare.com/client/v4/zones/${CF_ZONE_ID}/settings/browser_cache_ttl" \ 4 | -H "X-Auth-Email: ${CF_API_EMAIL}" \ 5 | -H "X-Auth-Key: ${CF_API_KEY}" \ 6 | -H "Content-Type: application/json" \ 7 | --data '{"value" 14400}' 8 | -------------------------------------------------------------------------------- /integration/tests/mobile-redirect/before.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | curl -X PATCH "https://api.cloudflare.com/client/v4/zones/${CF_ZONE_ID}/settings/mobile_redirect" \ 4 | -H "X-Auth-Email: ${CF_API_EMAIL}" \ 5 | -H "X-Auth-Key: ${CF_API_KEY}" \ 6 | -H "Content-Type: application/json" \ 7 | --data '{"value":{"status": "off"}}' 8 | -------------------------------------------------------------------------------- /integration/tests/minify/before.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | curl -X PATCH "https://api.cloudflare.com/client/v4/zones/${CF_ZONE_ID}/settings/minify" \ 4 | -H "X-Auth-Email: ${CF_API_EMAIL}" \ 5 | -H "X-Auth-Key: ${CF_API_KEY}" \ 6 | -H "Content-Type: application/json" \ 7 | --data '{"value":{"css": "off", "js": "off", "html": "off"}}' 8 | -------------------------------------------------------------------------------- /integration/tests/common.mk: -------------------------------------------------------------------------------- 1 | SHELL := /bin/bash 2 | 3 | .PHONY: commonrun 4 | commonrun: 5 | ./before.sh 6 | ../../../bin/integration run \ 7 | --spec ./spec.yaml \ 8 | --email $(CF_API_EMAIL) \ 9 | --key $(CF_API_KEY) \ 10 | --zone-name $(CF_ZONE_NAME) 11 | rm -rf out 12 | mkdir -p out 13 | ./after.sh > out/result.json 14 | ./verify.sh 15 | -------------------------------------------------------------------------------- /Dockerfile.manager: -------------------------------------------------------------------------------- 1 | # Copy kubeflare into a thin image 2 | FROM debian:buster 3 | WORKDIR / 4 | 5 | RUN apt-get update \ 6 | && apt-get -y install \ 7 | ca-certificates 8 | 9 | ADD ./bin/kubeflare /kubeflare 10 | 11 | RUN useradd -c 'kubeflare user' -m -d /home/kubeflare -s /bin/bash -u 1001 kubeflare 12 | USER kubeflare 13 | ENV HOME /home/kubeflare 14 | 15 | ENTRYPOINT ["/kubeflare", "manager"] 16 | -------------------------------------------------------------------------------- /deploy/Dockerfile.manager: -------------------------------------------------------------------------------- 1 | FROM ubuntu:latest 2 | 3 | RUN apt-get update -y && apt-get install -y ca-certificates 4 | 5 | WORKDIR / 6 | COPY kubeflare /kubeflare 7 | RUN chmod a+x /kubeflare 8 | 9 | RUN useradd -c 'kubeflare-manager user' -m -d /home/kubeflare-manager -s /bin/bash -u 1001 kubeflare-manager 10 | USER kubeflare-manager 11 | ENV HOME /home/kubeflare-manager 12 | 13 | ENTRYPOINT ["/kubeflare", "manager"] 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Binaries for programs and plugins 3 | *.exe 4 | *.exe~ 5 | *.dll 6 | *.so 7 | *.dylib 8 | bin 9 | 10 | # Test binary, build with `go test -c` 11 | *.test 12 | 13 | # Output of the go coverage tool, specifically when used with LiteIDE 14 | *.out 15 | 16 | # Kubernetes Generated files - skip generated files, except for vendored files 17 | 18 | !vendor/**/zz_generated.* 19 | 20 | # editor and IDE paraphernalia 21 | .idea 22 | *.swp 23 | *.swo 24 | *~ 25 | 26 | dist/ 27 | 28 | .DS_Store 29 | 30 | vendor 31 | imported -------------------------------------------------------------------------------- /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | name: build 2 | on: 3 | push: 4 | branches: 5 | - 'main' 6 | pull_request: 7 | branches: 8 | - 'main' 9 | 10 | jobs: 11 | build-docker: 12 | name: Build Docker image 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Set up Go 16 | uses: actions/setup-go@v1 17 | with: 18 | go-version: 1.16 19 | - name: Checkout code 20 | uses: actions/checkout@v2 21 | - name: Build Docker image 22 | run: | 23 | make image 24 | -------------------------------------------------------------------------------- /pkg/cli/kubeflarecli/version.go: -------------------------------------------------------------------------------- 1 | package kubeflarecli 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/spf13/cobra" 7 | 8 | "github.com/replicatedhq/kubeflare/pkg/version" 9 | ) 10 | 11 | func Version() *cobra.Command { 12 | cmd := &cobra.Command{ 13 | Use: "version", 14 | Short: "kubeflare version information", 15 | Long: `...`, 16 | SilenceErrors: true, 17 | RunE: func(cmd *cobra.Command, args []string) error { 18 | fmt.Printf("Kubeflare %s\n", version.Version()) 19 | return nil 20 | }, 21 | } 22 | 23 | return cmd 24 | } 25 | -------------------------------------------------------------------------------- /hack/boilerplate.go.txt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Replicated, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | -------------------------------------------------------------------------------- /pkg/apis/crds/group.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Replicated, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package crds contains crds API versions 18 | package crds 19 | -------------------------------------------------------------------------------- /docs/mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: Kubeflare Docs 2 | theme: 3 | name: material 4 | markdown_extensions: 5 | - admonition 6 | nav: 7 | - Getting Started: 8 | - 'Introduction': 'index.md' 9 | - 'Cloudflare API Support': 'getting-started/api-support.md' 10 | - 'Importing': 'getting-started/importing.md' 11 | - Installing: 12 | - 'Install with kubectl': 'install/kubectl.md' 13 | - 'Install with Helm': 'install/helm.md' 14 | - API: 15 | - 'APIToken': 'api/apitoken.md' 16 | - 'Zone': 'api/zone.md' 17 | - 'DNSRecord': 'api/dnsrecord.md' 18 | - 'PageRules': 'api/pagerules.md' 19 | - 'WAFRules': 'api/wafrules.md' 20 | - 'WorkerRoutes': 'api/workerroutes.md' 21 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: release 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | 8 | jobs: 9 | goreleaser: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v2 14 | - name: Set up Go 15 | uses: actions/setup-go@v2 16 | with: 17 | go-version: 1.16 18 | - name: Login to Docker Hub 19 | uses: azure/docker-login@v1 20 | with: 21 | username: ${{ secrets.DOCKERHUB_USER }} 22 | password: ${{ secrets.DOCKERHUB_PASSWORD }} 23 | - name: Run GoReleaser 24 | uses: goreleaser/goreleaser-action@v2 25 | with: 26 | args: release --rm-dist 27 | env: 28 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 29 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribuing Guide 2 | 3 | 4 | ### Integration tests 5 | 6 | These require a domain and API token from Cloudflare. 7 | There's a test domain in the GitHub repo and GitHub Actions uses it when running. 8 | 9 | To execute locally: 10 | 11 | ``` 12 | export CF_API_EMAIL=you@you.com 13 | export CF_API_KEY=1234 14 | export CF_ZONE_ID=12345 15 | export CF_ZONE_NAME=domain.com 16 | 17 | make integration 18 | ``` 19 | 20 | To run a single integration test (useful when adding a new api call): 21 | 22 | ``` 23 | export CF_API_EMAIL=you@you.com 24 | export CF_API_KEY=1234 25 | export CF_ZONE_ID=12345 26 | export CF_ZONE_NAME=domain.com 27 | 28 | make -C integration/tests/cache-level run 29 | ``` 30 | 31 | ## Setup 32 | 33 | - You should create an A record (proxied) for "mobile". 34 | -------------------------------------------------------------------------------- /pkg/client/kubeflareclientset/fake/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Replicated, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | // This package has the automatically generated fake clientset. 20 | package fake 21 | -------------------------------------------------------------------------------- /pkg/client/kubeflareclientset/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Replicated, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | // This package has the automatically generated clientset. 20 | package kubeflareclientset 21 | -------------------------------------------------------------------------------- /pkg/client/kubeflareclientset/typed/crds/v1alpha1/fake/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Replicated, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | // Package fake has the automatically generated clients. 20 | package fake 21 | -------------------------------------------------------------------------------- /pkg/client/kubeflareclientset/scheme/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Replicated, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | // This package contains the scheme of the automatically generated clientset. 20 | package scheme 21 | -------------------------------------------------------------------------------- /pkg/client/kubeflareclientset/typed/crds/v1alpha1/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Replicated, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | // This package has the automatically generated typed clients. 20 | package v1alpha1 21 | -------------------------------------------------------------------------------- /docs/docs/api/apitoken.md: -------------------------------------------------------------------------------- 1 | # APIToken 2 | 3 | A `kind: APIToken` resource represents a credential that can be used to call Cloudflare API. This token can be provided 4 | inline (not recommended) or referenced from a Kubernetes secret. 5 | 6 | ### Examples 7 | 8 | #### Inline 9 | 10 | ```yaml 11 | apiVersion: crds.kubeflare.io/v1alpha1 12 | kind: APIToken 13 | metadata: 14 | name: api-token-name 15 | spec: 16 | name: token-name 17 | email: email-address 18 | value: the-secret-api-token 19 | ``` 20 | 21 | #### Referencing a Kubernetes Secret 22 | 23 | ```yaml 24 | apiVersion: crds.kubeflare.io/v1alpha1 25 | kind: APIToken 26 | metadata: 27 | name: api-token-name 28 | spec: 29 | name: token-name 30 | email: email-address 31 | valueFrom: 32 | secretKeyRef: 33 | name: secret-name 34 | key: key-in-secret 35 | ``` 36 | -------------------------------------------------------------------------------- /pkg/apis/addtoscheme_crds_v1alpha1.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Replicated, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package apis 18 | 19 | import "github.com/replicatedhq/kubeflare/pkg/apis/crds/v1alpha1" 20 | 21 | func init() { 22 | // Register the types with the Scheme so the components can map objects to GroupVersionKinds and back 23 | AddToSchemes = append(AddToSchemes, v1alpha1.SchemeBuilder.AddToScheme) 24 | } 25 | -------------------------------------------------------------------------------- /pkg/controller/shared/strings.go: -------------------------------------------------------------------------------- 1 | package shared 2 | 3 | import "sort" 4 | 5 | func StringSlicesMatch(a []string, b []string) bool { 6 | // sort on a copy to preserve original order 7 | aa := make([]string, len(a)) 8 | bb := make([]string, len(b)) 9 | 10 | copy(aa, a) 11 | copy(bb, b) 12 | 13 | sort.Strings(aa) 14 | sort.Strings(bb) 15 | 16 | hasChanged := len(aa) != len(bb) 17 | 18 | if !hasChanged { 19 | for i, v := range aa { 20 | if v != bb[i] { 21 | hasChanged = true 22 | } 23 | } 24 | } 25 | 26 | return !hasChanged 27 | } 28 | 29 | func InterfaceArrayToStringArray(in []interface{}) []string { 30 | out := make([]string, len(in)) 31 | 32 | for k, v := range in { 33 | out[k] = v.(string) 34 | } 35 | 36 | return out 37 | } 38 | 39 | func StringArrayToInterfaceArray(in []string) []interface{} { 40 | out := make([]interface{}, len(in)) 41 | 42 | for k, v := range in { 43 | out[k] = v 44 | } 45 | 46 | return out 47 | } 48 | -------------------------------------------------------------------------------- /docs/docs/index.md: -------------------------------------------------------------------------------- 1 | # Home 2 | 3 | # The Kubeflare Docs 4 | 5 | Everything you need to know about Kubeflare, from installation to writing manifests and troubleshooting any problems. 6 | 7 | ## What is Kubeflare? 8 | 9 | Kubeflare is an [open-source](https://github.com/replicatedhq/kubeflare) [Kubernetes Operator](https://kubernetes.io/docs/concepts/extend-kubernetes/operator/) to configure a Cloudflare account. 10 | 11 | ## Getting Started 12 | 13 | New to Kubeflare? 14 | This is where you want to get started. 15 | 16 | - Installation: Install Kubeflare via [kubectl](/install/kubectl) or [Helm](/install/helm). 17 | - Tutorial: Learn how to create a simple A record with Kubeflare [Tutorial](/getting-started/tutorial) 18 | - Importing: Learn how to automatically [import your existing Cloudflare zones](/getting-started/importing) 19 | 20 | ## Getting Help 21 | 22 | Need more help? 23 | 24 | - Submit an issue on [GitHub](https://github.com/replicatedhq/kubeflare). 25 | -------------------------------------------------------------------------------- /pkg/apis/crds/v1alpha1/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Replicated, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // package v1alpha1 contains API Schema definitions for the crds v1alpha1 API group 18 | // +k8s:openapi-gen=true 19 | // +k8s:deepcopy-gen=package,register 20 | // +k8s:conversion-gen=github.com/replicatedhq/kubeflare/pkg/apis/crds 21 | // +k8s:defaulter-gen=TypeMeta 22 | // +groupName=crds.kubeflare.io 23 | package v1alpha1 24 | -------------------------------------------------------------------------------- /pkg/controller/shared/zone.go: -------------------------------------------------------------------------------- 1 | package shared 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/pkg/errors" 7 | crdsv1alpha1 "github.com/replicatedhq/kubeflare/pkg/apis/crds/v1alpha1" 8 | crdsclientv1alpha1 "github.com/replicatedhq/kubeflare/pkg/client/kubeflareclientset/typed/crds/v1alpha1" 9 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 10 | "sigs.k8s.io/controller-runtime/pkg/client/config" 11 | ) 12 | 13 | func GetZone(ctx context.Context, namespace string, zoneName string) (*crdsv1alpha1.Zone, error) { 14 | cfg, err := config.GetConfig() 15 | if err != nil { 16 | return nil, errors.Wrap(err, "failed to get config") 17 | } 18 | 19 | crdsClient, err := crdsclientv1alpha1.NewForConfig(cfg) 20 | if err != nil { 21 | return nil, errors.Wrap(err, "failed to create crds client") 22 | } 23 | 24 | zone, err := crdsClient.Zones(namespace).Get(ctx, zoneName, metav1.GetOptions{}) 25 | if err != nil { 26 | return nil, errors.Wrap(err, "failed to get zone") 27 | } 28 | 29 | return zone, nil 30 | } 31 | -------------------------------------------------------------------------------- /docs/docs/getting-started/api-support.md: -------------------------------------------------------------------------------- 1 | # Cloudflare API Support Matrix 2 | 3 | Cloudflare has a lot of objects and supports configuring them through their API. 4 | The Kubeflare controller is early and doesn't (yet) have support for all Cloudflare objects. 5 | Our intention is to support them all, including enterprise-only functionality. 6 | 7 | The table below is the current state of support for the Cloudflare API. 8 | If an object is not included on the table, it's not supported yet. 9 | Feel free to open an [issue](https://github.com/replicatedhq/kubeflare/issues/new) if there's a specific API you need or would like to help with. 10 | 11 | | Cloudflare API | Status | Kubeflare Version | 12 | |----------------|--------|-------------------| 13 | | Zone Settings | Completed | 0.1.0 | 14 | | DNS Records | Completed | 0.1.0 | 15 | | PageRules | In Progress | 0.1.0 | 16 | | Access Applications | In Progress | 0.1.0 | 17 | | Web Application Firewall | In Progress | 0.1.0 | 18 | | WorkerRoutes | Completed | (not released) | 19 | -------------------------------------------------------------------------------- /pkg/cli/integrationcli/root.go: -------------------------------------------------------------------------------- 1 | package integrationcli 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | 8 | "github.com/spf13/cobra" 9 | "github.com/spf13/viper" 10 | ) 11 | 12 | func RootCmd() *cobra.Command { 13 | cmd := &cobra.Command{ 14 | Use: "integration", 15 | Short: "this is the out-of-k8s integration and e2e tests of kubeflare", 16 | Long: `...`, 17 | PreRun: func(cmd *cobra.Command, args []string) { 18 | viper.BindPFlags(cmd.Flags()) 19 | }, 20 | Run: func(cmd *cobra.Command, args []string) { 21 | cmd.Help() 22 | os.Exit(1) 23 | }, 24 | } 25 | 26 | cobra.OnInitialize(initConfig) 27 | 28 | cmd.AddCommand(RunCmd()) 29 | 30 | viper.BindPFlags(cmd.Flags()) 31 | 32 | viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_")) 33 | 34 | return cmd 35 | } 36 | 37 | func InitAndExecute() { 38 | if err := RootCmd().Execute(); err != nil { 39 | fmt.Println(err) 40 | os.Exit(1) 41 | } 42 | } 43 | 44 | func initConfig() { 45 | viper.SetEnvPrefix("KUBEFLARE") 46 | viper.AutomaticEnv() 47 | } 48 | -------------------------------------------------------------------------------- /pkg/client/kubeflareclientset/typed/crds/v1alpha1/generated_expansion.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Replicated, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | package v1alpha1 20 | 21 | type APITokenExpansion interface{} 22 | 23 | type AccessApplicationExpansion interface{} 24 | 25 | type DNSRecordExpansion interface{} 26 | 27 | type PageRuleExpansion interface{} 28 | 29 | type WebApplicationFirewallRuleExpansion interface{} 30 | 31 | type WorkerRouteExpansion interface{} 32 | 33 | type ZoneExpansion interface{} 34 | -------------------------------------------------------------------------------- /pkg/controller/controller.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Replicated, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package controller 18 | 19 | import ( 20 | "sigs.k8s.io/controller-runtime/pkg/manager" 21 | ) 22 | 23 | // AddToManagerFuncs is a list of functions to add all Controllers to the Manager 24 | var AddToManagerFuncs []func(manager.Manager) error 25 | 26 | // AddToManager adds all Controllers to the Manager 27 | func AddToManager(m manager.Manager) error { 28 | for _, f := range AddToManagerFuncs { 29 | if err := f(m); err != nil { 30 | return err 31 | } 32 | } 33 | return nil 34 | } 35 | -------------------------------------------------------------------------------- /.goreleaser.yaml: -------------------------------------------------------------------------------- 1 | project_name: kubeflare 2 | release: 3 | github: 4 | owner: replicatedhq 5 | name: kubeflare 6 | builds: 7 | - id: kubeflare 8 | goos: 9 | - linux 10 | goarch: 11 | - amd64 12 | env: 13 | - CGO_ENABLED=0 14 | main: cmd/kubeflare/main.go 15 | ldflags: -s -w 16 | -X github.com/replicatedhq/kubeflare/pkg/version.version={{.Version}} 17 | -X github.com/replicatedhq/kubeflare/pkg/version.gitSHA={{.Commit}} 18 | -X github.com/replicatedhq/kubeflare/pkg/version.buildTime={{.Date}} 19 | -extldflags "-static" 20 | flags: -tags netgo -installsuffix netgo 21 | binary: kubeflare 22 | hooks: {} 23 | archives: 24 | - id: kubeflare 25 | builds: 26 | - kubeflare 27 | format: tar.gz 28 | name_template: "{{ .Binary }}_{{ .Os }}_{{ .Arch }}" 29 | files: 30 | - licence* 31 | - LICENCE* 32 | - license* 33 | - LICENSE* 34 | - readme* 35 | - README* 36 | - changelog* 37 | - CHANGELOG* 38 | dockers: 39 | - dockerfile: ./deploy/Dockerfile.manager 40 | image_templates: 41 | - "replicated/kubeflare-manager:{{.Version}}" 42 | binaries: 43 | - manager 44 | -------------------------------------------------------------------------------- /pkg/version/version.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | var ( 8 | build Build 9 | hasBuilt = false 10 | ) 11 | 12 | // Build holds details about this build of the Ship binary 13 | type Build struct { 14 | Version string `json:"version,omitempty"` 15 | GitSHA string `json:"git,omitempty"` 16 | BuildTime time.Time `json:"buildTime,omitempty"` 17 | TimeFallback string `json:"buildTimeFallback,omitempty"` 18 | } 19 | 20 | // Init sets up the version info from build args 21 | func Init() { 22 | build.Version = version 23 | if len(gitSHA) >= 7 { 24 | build.GitSHA = gitSHA[:7] 25 | } 26 | var err error 27 | build.BuildTime, err = time.Parse(time.RFC3339, buildTime) 28 | if err != nil { 29 | build.TimeFallback = buildTime 30 | } 31 | hasBuilt = true 32 | } 33 | 34 | // GetBuild gets the build 35 | func GetBuild() Build { 36 | if !hasBuilt { 37 | Init() 38 | } 39 | return build 40 | } 41 | 42 | // Version gets the version 43 | func Version() string { 44 | if !hasBuilt { 45 | Init() 46 | } 47 | return build.Version 48 | } 49 | 50 | // GitSHA gets the gitsha 51 | func GitSHA() string { 52 | if !hasBuilt { 53 | Init() 54 | } 55 | return build.GitSHA 56 | } 57 | 58 | // BuildTime gets the build time 59 | func BuildTime() time.Time { 60 | if !hasBuilt { 61 | Init() 62 | } 63 | return build.BuildTime 64 | } 65 | -------------------------------------------------------------------------------- /pkg/logger/logger.go: -------------------------------------------------------------------------------- 1 | package logger 2 | 3 | import ( 4 | "os" 5 | 6 | "go.uber.org/zap" 7 | "go.uber.org/zap/zapcore" 8 | ) 9 | 10 | var log *zap.Logger 11 | var atom zap.AtomicLevel 12 | 13 | func init() { 14 | atom = zap.NewAtomicLevel() 15 | atom.SetLevel(zapcore.InfoLevel) 16 | 17 | encoderCfg := zap.NewProductionEncoderConfig() 18 | encoderCfg.TimeKey = "" 19 | 20 | l := zap.New(zapcore.NewCore( 21 | zapcore.NewJSONEncoder(encoderCfg), 22 | zapcore.Lock(os.Stdout), 23 | atom, 24 | )) 25 | defer l.Sync() 26 | 27 | log = l 28 | } 29 | 30 | func SetDebug() { 31 | atom.SetLevel(zapcore.DebugLevel) 32 | } 33 | 34 | func Error(err error, fields ...zap.Field) { 35 | defer log.Sync() 36 | log.Error(err.Error(), fields...) 37 | } 38 | 39 | func Warn(msg string, fields ...zap.Field) { 40 | defer log.Sync() 41 | log.Warn(msg, fields...) 42 | } 43 | 44 | func Info(msg string, fields ...zap.Field) { 45 | defer log.Sync() 46 | log.Info(msg, fields...) 47 | } 48 | 49 | func Infof(template string, args ...interface{}) { 50 | defer log.Sync() 51 | sugar := log.Sugar() 52 | sugar.Infof(template, args) 53 | } 54 | 55 | func Debug(msg string, fields ...zap.Field) { 56 | defer log.Sync() 57 | log.Debug(msg, fields...) 58 | } 59 | 60 | func Debugf(template string, args ...interface{}) { 61 | defer log.Sync() 62 | sugar := log.Sugar() 63 | sugar.Debugf(template, args) 64 | } 65 | -------------------------------------------------------------------------------- /docs/docs/getting-started/tutorial.md: -------------------------------------------------------------------------------- 1 | # Kubeflare Tutorial 2 | 3 | This tutorial describes how to setup Kubeflare to manage your Cloudflare resources. 4 | 5 | ## Cloudflare Account Setup 6 | 7 | If you don't have a Cloudflare account and site already, create one following the instructions [here] (https://support.cloudflare.com/hc/en-us/articles/201720164-Creating-a-Cloudflare-account-and-adding-a-website) 8 | 9 | ## Install 10 | 11 | Install Kubeflare via [kubectl](/install/kubectl) or [Helm](/install/helm). 12 | 13 | ## Create a DNS Record 14 | 15 | ### Create a secret with your API Token 16 | 17 | kubectl -n kubeflare-system create secret generic cf-api-secret --from-literal cf-api-token= 18 | 19 | ### Define a Zone 20 | 21 | ```yaml 22 | apiVersion: crds.kubeflare.io/v1alpha1 23 | kind: Zone 24 | metadata: 25 | name: domainname.io 26 | spec: 27 | apiToken: 28 | valueFrom: 29 | secretKeyRef: 30 | name: cf-api-secret 31 | key: cf-api-token 32 | ``` 33 | 34 | Full API Documentation for Zones in Kubeflare [here] (/api/zone). 35 | 36 | ### Create an A Record 37 | 38 | ```yaml 39 | apiVersion: crds.kubeflare.io/v1alpha1 40 | kind: DNSRecord 41 | metadata: 42 | name: www.domainname.io 43 | spec: 44 | zone: domainname.io 45 | record: 46 | type: "A" 47 | name: "www" 48 | content: "1.1.1.1" 49 | proxied: true 50 | ttl: 3600 51 | ``` 52 | -------------------------------------------------------------------------------- /pkg/cli/kubeflarecli/root.go: -------------------------------------------------------------------------------- 1 | package kubeflarecli 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | 8 | "github.com/spf13/cobra" 9 | "github.com/spf13/viper" 10 | "k8s.io/cli-runtime/pkg/genericclioptions" 11 | ) 12 | 13 | var ( 14 | kubernetesConfigFlags *genericclioptions.ConfigFlags 15 | ) 16 | 17 | func RootCmd() *cobra.Command { 18 | cmd := &cobra.Command{ 19 | Use: "manager", 20 | Short: "kubeflare is a Kubernetes Operator to manage Cloudflare settings", 21 | Long: `...`, 22 | PreRun: func(cmd *cobra.Command, args []string) { 23 | viper.BindPFlags(cmd.Flags()) 24 | }, 25 | Run: func(cmd *cobra.Command, args []string) { 26 | cmd.Help() 27 | os.Exit(1) 28 | }, 29 | } 30 | 31 | cobra.OnInitialize(initConfig) 32 | 33 | kubernetesConfigFlags = genericclioptions.NewConfigFlags(false) 34 | kubernetesConfigFlags.AddFlags(cmd.PersistentFlags()) 35 | 36 | cmd.PersistentFlags().String("log-level", "info", "set the log level") 37 | 38 | cmd.AddCommand(Version()) 39 | cmd.AddCommand(ManagerCmd()) 40 | cmd.AddCommand(ImportCmd()) 41 | 42 | viper.BindPFlags(cmd.Flags()) 43 | 44 | viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_")) 45 | 46 | return cmd 47 | } 48 | 49 | func InitAndExecute() { 50 | if err := RootCmd().Execute(); err != nil { 51 | fmt.Println(err) 52 | os.Exit(1) 53 | } 54 | } 55 | 56 | func initConfig() { 57 | viper.SetEnvPrefix("KUBEFLARE") 58 | viper.AutomaticEnv() 59 | } 60 | -------------------------------------------------------------------------------- /pkg/cloudflare/dns/import.go: -------------------------------------------------------------------------------- 1 | package dns 2 | 3 | import ( 4 | "context" 5 | "github.com/cloudflare/cloudflare-go" 6 | "github.com/pkg/errors" 7 | "github.com/replicatedhq/kubeflare/pkg/apis/crds/v1alpha1" 8 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 9 | ) 10 | 11 | func FetchDNSRecordsForZone(token string, zone string, zoneID string) ([]*v1alpha1.DNSRecord, error) { 12 | cf, err := cloudflare.NewWithAPIToken(token) 13 | if err != nil { 14 | return nil, errors.Wrap(err, "create clouflare client") 15 | } 16 | 17 | resources, err := cf.DNSRecords(context.Background(), zoneID, cloudflare.DNSRecord{}) 18 | if err != nil { 19 | return nil, errors.Wrap(err, "fetch resources") 20 | } 21 | 22 | dnsRecords := []*v1alpha1.DNSRecord{} 23 | for _, resource := range resources { 24 | priority := int(*resource.Priority) 25 | dnsRecord := v1alpha1.DNSRecord{ 26 | TypeMeta: metav1.TypeMeta{ 27 | APIVersion: "crds.kubeflare.io/v1alpha1", 28 | Kind: "DNSRecord", 29 | }, 30 | ObjectMeta: metav1.ObjectMeta{ 31 | Name: resource.Name, 32 | }, 33 | Spec: v1alpha1.DNSRecordSpec{ 34 | Zone: zone, 35 | Record: &v1alpha1.Record{ 36 | Type: resource.Type, 37 | Name: resource.Name, 38 | Content: resource.Content, 39 | TTL: &resource.TTL, 40 | Priority: &priority, 41 | Proxied: resource.Proxied, 42 | }, 43 | }, 44 | } 45 | 46 | dnsRecords = append(dnsRecords, &dnsRecord) 47 | } 48 | 49 | return dnsRecords, nil 50 | } 51 | -------------------------------------------------------------------------------- /pkg/cloudflare/workerroute/import.go: -------------------------------------------------------------------------------- 1 | package workerroute 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "github.com/cloudflare/cloudflare-go" 8 | "github.com/pkg/errors" 9 | "github.com/replicatedhq/kubeflare/pkg/apis/crds/v1alpha1" 10 | "github.com/replicatedhq/kubeflare/pkg/internal" 11 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 12 | ) 13 | 14 | func FetchWorkerRoutesForZone(token string, zone string, zoneID string) ([]*v1alpha1.WorkerRoute, error) { 15 | cf, err := cloudflare.NewWithAPIToken(token) 16 | if err != nil { 17 | return nil, errors.Wrap(err, "create clouflare client") 18 | } 19 | 20 | resources, err := cf.ListWorkerRoutes(context.Background(), zoneID) 21 | if err != nil { 22 | return nil, errors.Wrap(err, "fetch resources") 23 | } 24 | 25 | workerRoutes := []*v1alpha1.WorkerRoute{} 26 | for i, resource := range resources.Routes { 27 | workerRoute := v1alpha1.WorkerRoute{ 28 | TypeMeta: metav1.TypeMeta{ 29 | APIVersion: "crds.kubeflare.io/v1alpha1", 30 | Kind: "WorkerRoute", 31 | }, 32 | ObjectMeta: metav1.ObjectMeta{ 33 | Name: fmt.Sprintf("workerroute-%d", i), 34 | Annotations: map[string]string{ 35 | internal.ImportedIDAnnotation: resource.ID, 36 | }, 37 | }, 38 | Spec: v1alpha1.WorkerRouteSpec{ 39 | Zone: zone, 40 | Pattern: resource.Pattern, 41 | Script: resource.Script, 42 | }, 43 | } 44 | 45 | workerRoutes = append(workerRoutes, &workerRoute) 46 | } 47 | 48 | return workerRoutes, nil 49 | } 50 | -------------------------------------------------------------------------------- /pkg/webhook/webhook.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Replicated, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package webhook 18 | 19 | import ( 20 | "sigs.k8s.io/controller-runtime/pkg/manager" 21 | ) 22 | 23 | // AddToManagerFuncs is a list of functions to add all Controllers to the Manager 24 | var AddToManagerFuncs []func(manager.Manager) error 25 | 26 | // AddToManager adds all Controllers to the Manager 27 | // +kubebuilder:rbac:groups=admissionregistration.k8s.io,resources=mutatingwebhookconfigurations;validatingwebhookconfigurations,verbs=get;list;watch;create;update;patch;delete 28 | // +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;create;update;patch;delete 29 | // +kubebuilder:rbac:groups="",resources=services,verbs=get;list;watch;create;update;patch;delete 30 | func AddToManager(m manager.Manager) error { 31 | for _, f := range AddToManagerFuncs { 32 | if err := f(m); err != nil { 33 | return err 34 | } 35 | } 36 | return nil 37 | } 38 | -------------------------------------------------------------------------------- /pkg/apis/apis.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Replicated, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Generate deepcopy for apis 18 | //go:generate go run ../../vendor/k8s.io/code-generator/cmd/deepcopy-gen/main.go -O zz_generated.deepcopy -i ./... -h ../../hack/boilerplate.go.txt 19 | //go:generate go run ../../vendor/k8s.io/code-generator/cmd/client-gen/main.go --output-package=github.com/replicatedhq/kubeflare/pkg/client --clientset-name kubeflareclientset --input-base github.com/replicatedhq/kubeflare/pkg/apis --input crds/v1alpha1 -h ../../hack/boilerplate.go.txt 20 | 21 | // Package apis contains Kubernetes API groups. 22 | package apis 23 | 24 | import ( 25 | "k8s.io/apimachinery/pkg/runtime" 26 | ) 27 | 28 | // AddToSchemes may be used to add all resources defined in the project to a Scheme 29 | var AddToSchemes runtime.SchemeBuilder 30 | 31 | // AddToScheme adds all Resources to the Scheme 32 | func AddToScheme(s *runtime.Scheme) error { 33 | return AddToSchemes.AddToScheme(s) 34 | } 35 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/replicatedhq/kubeflare 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/cloudflare/cloudflare-go v0.31.0 7 | github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect 8 | github.com/pkg/errors v0.9.1 9 | github.com/spf13/cobra v1.2.1 10 | github.com/spf13/viper v1.8.1 11 | github.com/stretchr/testify v1.7.0 12 | go.uber.org/zap v1.19.0 13 | gotest.tools v2.2.0+incompatible 14 | k8s.io/api v0.19.16 15 | k8s.io/apimachinery v0.19.16 16 | k8s.io/cli-runtime v0.19.16 17 | k8s.io/client-go v0.19.16 18 | sigs.k8s.io/controller-runtime v0.6.5 19 | ) 20 | 21 | require ( 22 | github.com/evanphx/json-patch v4.11.0+incompatible // indirect 23 | github.com/go-openapi/spec v0.19.5 // indirect 24 | github.com/googleapis/gnostic v0.5.5 // indirect 25 | github.com/imdario/mergo v0.3.12 // indirect 26 | github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect 27 | github.com/prometheus/client_golang v1.11.0 // indirect 28 | golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect 29 | golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 // indirect 30 | gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect 31 | k8s.io/code-generator v0.19.17-rc.0 // indirect 32 | k8s.io/klog/v2 v2.8.0 // indirect 33 | k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7 // indirect 34 | k8s.io/utils v0.0.0-20210802155522-efc7438f0176 // indirect 35 | sigs.k8s.io/controller-tools v0.4.1 // indirect 36 | ) 37 | 38 | replace github.com/appscode/jsonpatch => github.com/gomodules/jsonpatch v2.0.1+incompatible 39 | -------------------------------------------------------------------------------- /docs/docs/api/wafrules.md: -------------------------------------------------------------------------------- 1 | # WAF Rule 2 | 3 | A `kind: WebApplicationFirewallRule` resource will manage one or more WAF rules in a managed [zone](../zone). 4 | Each resource can contain one or more WAF rules when specified under `spec.rules`. 5 | 6 | ## Attributes 7 | 8 | ### Zone 9 | 10 | Each `kind: WebApplicationFirewallRule` must contain a `spec.zone` string attribute. 11 | The value of this attribute must match a [zone](../zone) managed by Kubeflare. 12 | The API token to manage the WAF rule(s) will be read from the associated Zone kind resource. 13 | 14 | ### Rule(s) 15 | 16 | For more information on this type, see the [Cloudflare documentation](https://api.cloudflare.com/#waf-rules-edit-rule). 17 | 18 | The following attributes are supported in the `rules` object: 19 | 20 | | Name | Type | Description | 21 | |------|------|-------------| 22 | | id | string | The WAF rule ID 23 | | mode | string | The WAF rule mode 24 | | packageid | string | The WAF rule package (optional) 25 | 26 | ## Examples 27 | 28 | ### Single WAF Rule 29 | 30 | The following example will set the mode for WAF Rule `PHP10001` to `simulate`: 31 | 32 | ```yaml 33 | apiVersion: crds.kubeflare.io/v1alpha1 34 | kind: WebApplicationFirewallRule 35 | metadata: 36 | name: php-100001 37 | spec: 38 | zone: domainname.io 39 | rules: 40 | - id: "PHP100001" 41 | mode: "simulate" 42 | ``` 43 | 44 | ### Multiple WAF Rules 45 | 46 | The following example will configure multiple PHP WAF Rules for a domain: 47 | 48 | ```yaml 49 | apiVersion: crds.kubeflare.io/v1alpha1 50 | kind: WebApplicationFirewallRule 51 | metadata: 52 | name: php-rules 53 | spec: 54 | zone: domainname.io 55 | rules: 56 | - id: "PHP100001" 57 | mode: "simulate" 58 | - id: "PHP100011" 59 | mode: "challenge" 60 | ``` 61 | -------------------------------------------------------------------------------- /pkg/apis/crds/v1alpha1/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Replicated, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // NOTE: Boilerplate only. Ignore this file. 18 | 19 | // package v1alpha1 contains API Schema definitions for the crds v1alpha1 API group 20 | // +k8s:openapi-gen=true 21 | // +k8s:deepcopy-gen=package,register 22 | // +k8s:conversion-gen=github.com/replicatedhq/kubeflare/pkg/apis/crds 23 | // +k8s:defaulter-gen=TypeMeta 24 | // +groupName=crds.kubeflare.io 25 | package v1alpha1 26 | 27 | import ( 28 | "k8s.io/apimachinery/pkg/runtime/schema" 29 | "sigs.k8s.io/controller-runtime/pkg/scheme" 30 | ) 31 | 32 | var ( 33 | // SchemeGroupVersion is group version used to register these objects 34 | SchemeGroupVersion = schema.GroupVersion{Group: "crds.kubeflare.io", Version: "v1alpha1"} 35 | 36 | // SchemeBuilder is used to add go types to the GroupVersionKind scheme 37 | SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion} 38 | 39 | // AddToScheme is required by pkg/client/... 40 | AddToScheme = SchemeBuilder.AddToScheme 41 | ) 42 | 43 | // Resource is required by pkg/client/listers/... 44 | func Resource(resource string) schema.GroupResource { 45 | return SchemeGroupVersion.WithResource(resource).GroupResource() 46 | } 47 | -------------------------------------------------------------------------------- /pkg/controller/shared/cloudflare.go: -------------------------------------------------------------------------------- 1 | package shared 2 | 3 | import ( 4 | "context" 5 | "github.com/cloudflare/cloudflare-go" 6 | "github.com/pkg/errors" 7 | crdsclientv1alpha1 "github.com/replicatedhq/kubeflare/pkg/client/kubeflareclientset/typed/crds/v1alpha1" 8 | "github.com/replicatedhq/kubeflare/pkg/logger" 9 | "go.uber.org/zap" 10 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 11 | "sigs.k8s.io/controller-runtime/pkg/client/config" 12 | ) 13 | 14 | var HasDependenciesError = errors.New("dependency detected") 15 | 16 | func GetCloudflareAPI(ctx context.Context, namespace string, apiTokenName string) (*cloudflare.API, error) { 17 | crdsClient, err := GetCrdClient() 18 | if err != nil { 19 | return nil, err 20 | } 21 | 22 | apiToken, err := crdsClient.APITokens(namespace).Get(ctx, apiTokenName, metav1.GetOptions{}) 23 | if err != nil { 24 | return nil, errors.Wrap(err, "failed to get api token") 25 | } 26 | 27 | tokenValue, err := apiToken.GetTokenValue(ctx) 28 | if err != nil { 29 | return nil, errors.Wrap(err, "failed to get token value") 30 | } 31 | 32 | logger.Debug("creating cloudflare api object", 33 | zap.String("email", apiToken.Spec.Email), 34 | zap.Int("tokenLength", len(tokenValue))) 35 | 36 | api, err := cloudflare.NewWithAPIToken(tokenValue) 37 | if err != nil { 38 | return nil, errors.Wrap(err, "failed to create cloudflare api instance") 39 | } 40 | 41 | return api, nil 42 | } 43 | 44 | func GetCrdClient() (*crdsclientv1alpha1.CrdsV1alpha1Client, error) { 45 | cfg, err := config.GetConfig() 46 | if err != nil { 47 | return nil, errors.Wrap(err, "failed to get config") 48 | } 49 | 50 | crdsClient, err := crdsclientv1alpha1.NewForConfig(cfg) 51 | if err != nil { 52 | return nil, errors.Wrap(err, "failed to create crds client") 53 | } 54 | 55 | return crdsClient, nil 56 | } 57 | -------------------------------------------------------------------------------- /pkg/cli/integrationcli/run.go: -------------------------------------------------------------------------------- 1 | package integrationcli 2 | 3 | import ( 4 | "context" 5 | "io/ioutil" 6 | 7 | "github.com/cloudflare/cloudflare-go" 8 | crdsv1alpha1 "github.com/replicatedhq/kubeflare/pkg/apis/crds/v1alpha1" 9 | kubeflarescheme "github.com/replicatedhq/kubeflare/pkg/client/kubeflareclientset/scheme" 10 | "github.com/replicatedhq/kubeflare/pkg/controller/zone" 11 | "github.com/spf13/cobra" 12 | "github.com/spf13/viper" 13 | _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" 14 | ) 15 | 16 | func RunCmd() *cobra.Command { 17 | cmd := &cobra.Command{ 18 | Use: "run", 19 | Short: "runs a single integration test from a definition", 20 | Long: `...`, 21 | SilenceErrors: true, 22 | SilenceUsage: true, 23 | PreRun: func(cmd *cobra.Command, args []string) { 24 | viper.BindPFlags(cmd.Flags()) 25 | }, 26 | RunE: func(cmd *cobra.Command, args []string) error { 27 | v := viper.GetViper() 28 | 29 | b, err := ioutil.ReadFile(v.GetString("spec")) 30 | if err != nil { 31 | return err 32 | } 33 | 34 | decode := kubeflarescheme.Codecs.UniversalDeserializer().Decode 35 | obj, _, err := decode(b, nil, nil) 36 | if err != nil { 37 | return err 38 | } 39 | instance := obj.(*crdsv1alpha1.Zone) 40 | instance.Name = v.GetString("zone-name") 41 | 42 | ctx := context.TODO() 43 | 44 | cf, err := cloudflare.New(v.GetString("key"), v.GetString("email")) 45 | if err != nil { 46 | return err 47 | } 48 | 49 | err = zone.ReconcileSettings(ctx, instance, cf) 50 | if err != nil { 51 | return err 52 | } 53 | 54 | return nil 55 | }, 56 | } 57 | 58 | cmd.Flags().String("spec", "", "the spec file to apply") 59 | cmd.MarkFlagRequired("spec") 60 | cmd.Flags().String("email", "", "email to use with cloudflare") 61 | cmd.MarkFlagRequired("email") 62 | cmd.Flags().String("key", "", "cloudflare api key") 63 | cmd.MarkFlagRequired("key") 64 | cmd.Flags().String("zone-name", "", "zone name (domain) to use") 65 | cmd.MarkFlagRequired("zone-name") 66 | 67 | return cmd 68 | } 69 | -------------------------------------------------------------------------------- /pkg/client/kubeflareclientset/fake/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Replicated, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | package fake 20 | 21 | import ( 22 | crdsv1alpha1 "github.com/replicatedhq/kubeflare/pkg/apis/crds/v1alpha1" 23 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 24 | runtime "k8s.io/apimachinery/pkg/runtime" 25 | schema "k8s.io/apimachinery/pkg/runtime/schema" 26 | serializer "k8s.io/apimachinery/pkg/runtime/serializer" 27 | utilruntime "k8s.io/apimachinery/pkg/util/runtime" 28 | ) 29 | 30 | var scheme = runtime.NewScheme() 31 | var codecs = serializer.NewCodecFactory(scheme) 32 | var parameterCodec = runtime.NewParameterCodec(scheme) 33 | var localSchemeBuilder = runtime.SchemeBuilder{ 34 | crdsv1alpha1.AddToScheme, 35 | } 36 | 37 | // AddToScheme adds all types of this clientset into the given scheme. This allows composition 38 | // of clientsets, like in: 39 | // 40 | // import ( 41 | // "k8s.io/client-go/kubernetes" 42 | // clientsetscheme "k8s.io/client-go/kubernetes/scheme" 43 | // aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" 44 | // ) 45 | // 46 | // kclientset, _ := kubernetes.NewForConfig(c) 47 | // _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) 48 | // 49 | // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types 50 | // correctly. 51 | var AddToScheme = localSchemeBuilder.AddToScheme 52 | 53 | func init() { 54 | v1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"}) 55 | utilruntime.Must(AddToScheme(scheme)) 56 | } 57 | -------------------------------------------------------------------------------- /pkg/client/kubeflareclientset/scheme/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Replicated, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | package scheme 20 | 21 | import ( 22 | crdsv1alpha1 "github.com/replicatedhq/kubeflare/pkg/apis/crds/v1alpha1" 23 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 24 | runtime "k8s.io/apimachinery/pkg/runtime" 25 | schema "k8s.io/apimachinery/pkg/runtime/schema" 26 | serializer "k8s.io/apimachinery/pkg/runtime/serializer" 27 | utilruntime "k8s.io/apimachinery/pkg/util/runtime" 28 | ) 29 | 30 | var Scheme = runtime.NewScheme() 31 | var Codecs = serializer.NewCodecFactory(Scheme) 32 | var ParameterCodec = runtime.NewParameterCodec(Scheme) 33 | var localSchemeBuilder = runtime.SchemeBuilder{ 34 | crdsv1alpha1.AddToScheme, 35 | } 36 | 37 | // AddToScheme adds all types of this clientset into the given scheme. This allows composition 38 | // of clientsets, like in: 39 | // 40 | // import ( 41 | // "k8s.io/client-go/kubernetes" 42 | // clientsetscheme "k8s.io/client-go/kubernetes/scheme" 43 | // aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" 44 | // ) 45 | // 46 | // kclientset, _ := kubernetes.NewForConfig(c) 47 | // _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) 48 | // 49 | // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types 50 | // correctly. 51 | var AddToScheme = localSchemeBuilder.AddToScheme 52 | 53 | func init() { 54 | v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"}) 55 | utilruntime.Must(AddToScheme(Scheme)) 56 | } 57 | -------------------------------------------------------------------------------- /pkg/apis/crds/v1alpha1/dnsrecord_types.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Replicated, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1alpha1 18 | 19 | import ( 20 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 21 | ) 22 | 23 | type Record struct { 24 | Type string `json:"type"` 25 | Name string `json:"name"` 26 | Content string `json:"content"` 27 | TTL *int `json:"ttl,omitempty"` 28 | Priority *int `json:"priority,omitempty"` 29 | Proxied *bool `json:"proxied,omitempty"` 30 | } 31 | 32 | // DNSRecordSpec defines the desired state of DNSRecord 33 | type DNSRecordSpec struct { 34 | Zone string `json:"zone"` 35 | Record *Record `json:"record,omitempty"` 36 | Records []*Record `json:"records,omitempty"` 37 | } 38 | 39 | // DNSRecordStatus defines the observed state of DNSRecord 40 | type DNSRecordStatus struct { 41 | } 42 | 43 | // +genclient 44 | // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object 45 | 46 | // DNSRecord is the Schema for the dnsrecords API 47 | // +k8s:openapi-gen=true 48 | // +kubebuilder:subresource:status 49 | type DNSRecord struct { 50 | metav1.TypeMeta `json:",inline"` 51 | metav1.ObjectMeta `json:"metadata,omitempty"` 52 | 53 | Spec DNSRecordSpec `json:"spec,omitempty"` 54 | Status DNSRecordStatus `json:"status,omitempty"` 55 | } 56 | 57 | // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object 58 | 59 | // DNSRecordList contains a list of DNSRecord 60 | type DNSRecordList struct { 61 | metav1.TypeMeta `json:",inline"` 62 | metav1.ListMeta `json:"metadata,omitempty"` 63 | Items []DNSRecord `json:"items"` 64 | } 65 | 66 | func init() { 67 | SchemeBuilder.Register(&DNSRecord{}, &DNSRecordList{}) 68 | } 69 | -------------------------------------------------------------------------------- /docs/docs/install/kubectl.md: -------------------------------------------------------------------------------- 1 | # Install Kubeflare using Kubectl 2 | 3 | Kubeflare requires Kubernetes 1.16 or later to install. 4 | 5 | To install the current version of Kubeflare: 6 | 7 | ```shell 8 | git clone git@github.com:replicatedhq/kubeflare.git 9 | kubectl apply -f kubeflare/config/crds/v1 10 | cat < 0 { 61 | if configShallowCopy.Burst <= 0 { 62 | return nil, fmt.Errorf("burst is required to be greater than 0 when RateLimiter is not set and QPS is set to greater than 0") 63 | } 64 | configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst) 65 | } 66 | var cs Clientset 67 | var err error 68 | cs.crdsV1alpha1, err = crdsv1alpha1.NewForConfig(&configShallowCopy) 69 | if err != nil { 70 | return nil, err 71 | } 72 | 73 | cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy) 74 | if err != nil { 75 | return nil, err 76 | } 77 | return &cs, nil 78 | } 79 | 80 | // NewForConfigOrDie creates a new Clientset for the given config and 81 | // panics if there is an error in the config. 82 | func NewForConfigOrDie(c *rest.Config) *Clientset { 83 | var cs Clientset 84 | cs.crdsV1alpha1 = crdsv1alpha1.NewForConfigOrDie(c) 85 | 86 | cs.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c) 87 | return &cs 88 | } 89 | 90 | // New creates a new Clientset for the given RESTClient. 91 | func New(c rest.Interface) *Clientset { 92 | var cs Clientset 93 | cs.crdsV1alpha1 = crdsv1alpha1.New(c) 94 | 95 | cs.DiscoveryClient = discovery.NewDiscoveryClient(c) 96 | return &cs 97 | } 98 | -------------------------------------------------------------------------------- /config/crds/v1/crds.kubeflare.io_dnsrecords.yaml: -------------------------------------------------------------------------------- 1 | 2 | --- 3 | apiVersion: apiextensions.k8s.io/v1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | annotations: 7 | controller-gen.kubebuilder.io/version: v0.4.1 8 | creationTimestamp: null 9 | name: dnsrecords.crds.kubeflare.io 10 | spec: 11 | group: crds.kubeflare.io 12 | names: 13 | kind: DNSRecord 14 | listKind: DNSRecordList 15 | plural: dnsrecords 16 | singular: dnsrecord 17 | scope: Namespaced 18 | versions: 19 | - name: v1alpha1 20 | schema: 21 | openAPIV3Schema: 22 | description: DNSRecord is the Schema for the dnsrecords API 23 | properties: 24 | apiVersion: 25 | description: 'APIVersion defines the versioned schema of this representation 26 | of an object. Servers should convert recognized schemas to the latest 27 | internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' 28 | type: string 29 | kind: 30 | description: 'Kind is a string value representing the REST resource this 31 | object represents. Servers may infer this from the endpoint the client 32 | submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' 33 | type: string 34 | metadata: 35 | type: object 36 | spec: 37 | description: DNSRecordSpec defines the desired state of DNSRecord 38 | properties: 39 | record: 40 | properties: 41 | content: 42 | type: string 43 | name: 44 | type: string 45 | priority: 46 | type: integer 47 | proxied: 48 | type: boolean 49 | ttl: 50 | type: integer 51 | type: 52 | type: string 53 | required: 54 | - content 55 | - name 56 | - type 57 | type: object 58 | records: 59 | items: 60 | properties: 61 | content: 62 | type: string 63 | name: 64 | type: string 65 | priority: 66 | type: integer 67 | proxied: 68 | type: boolean 69 | ttl: 70 | type: integer 71 | type: 72 | type: string 73 | required: 74 | - content 75 | - name 76 | - type 77 | type: object 78 | type: array 79 | zone: 80 | type: string 81 | required: 82 | - zone 83 | type: object 84 | status: 85 | description: DNSRecordStatus defines the observed state of DNSRecord 86 | type: object 87 | type: object 88 | served: true 89 | storage: true 90 | subresources: 91 | status: {} 92 | status: 93 | acceptedNames: 94 | kind: "" 95 | plural: "" 96 | conditions: [] 97 | storedVersions: [] 98 | -------------------------------------------------------------------------------- /pkg/controller/accessapplication/access_policies.go: -------------------------------------------------------------------------------- 1 | package accessapplication 2 | 3 | import ( 4 | "github.com/cloudflare/cloudflare-go" 5 | "github.com/pkg/errors" 6 | crdsv1alpha1 "github.com/replicatedhq/kubeflare/pkg/apis/crds/v1alpha1" 7 | "github.com/replicatedhq/kubeflare/pkg/controller/shared" 8 | ) 9 | 10 | func diffAccessPolicies(existings []cloudflare.AccessPolicy, desireds []crdsv1alpha1.AccessPolicy) ([]cloudflare.AccessPolicy, []cloudflare.AccessPolicy, []cloudflare.AccessPolicy, error) { 11 | toCreate := []cloudflare.AccessPolicy{} 12 | toUpdate := []cloudflare.AccessPolicy{} 13 | toDelete := []cloudflare.AccessPolicy{} 14 | 15 | if len(existings) == 0 && len(desireds) == 0 { 16 | return toCreate, toUpdate, toDelete, nil 17 | } 18 | 19 | for _, existing := range existings { 20 | found := false 21 | for _, desired := range desireds { 22 | // TODO we should store these by ID in the status field 23 | // with this implementation, renaming would delete and recreate 24 | if desired.Name == existing.Name { 25 | found = true 26 | 27 | updated, err := diffAccessPolicy(existing, desired) 28 | if err != nil { 29 | return nil, nil, nil, errors.Wrap(err, "failed to diff policies") 30 | } 31 | 32 | if updated != nil { 33 | toUpdate = append(toUpdate, *updated) 34 | } 35 | 36 | goto Found 37 | } 38 | } 39 | 40 | Found: 41 | if !found { 42 | toDelete = append(toDelete, existing) 43 | } 44 | } 45 | 46 | for _, desired := range desireds { 47 | found := false 48 | for _, existing := range existings { 49 | if existing.Name == desired.Name { 50 | found = true 51 | goto Found2 52 | } 53 | } 54 | 55 | Found2: 56 | if !found { 57 | create := cloudflare.AccessPolicy{ 58 | Name: desired.Name, 59 | Decision: desired.Decision, 60 | Include: shared.StringArrayToInterfaceArray(desired.Include), 61 | Exclude: shared.StringArrayToInterfaceArray(desired.Exclude), 62 | Require: shared.StringArrayToInterfaceArray(desired.Require), 63 | } 64 | 65 | if desired.Precedence != nil { 66 | create.Precedence = *desired.Precedence 67 | } 68 | 69 | toCreate = append(toCreate, create) 70 | } 71 | } 72 | return toCreate, toUpdate, toDelete, nil 73 | } 74 | 75 | // diffAccessPolicy will diff existing to desired. 76 | // if there are diffs, it will return not-nil in the first response param 77 | func diffAccessPolicy(existing cloudflare.AccessPolicy, desired crdsv1alpha1.AccessPolicy) (*cloudflare.AccessPolicy, error) { 78 | hasChanged := false 79 | 80 | if existing.Name != desired.Name { 81 | hasChanged = true 82 | existing.Name = desired.Name 83 | } 84 | 85 | if existing.Decision != desired.Decision { 86 | hasChanged = true 87 | existing.Decision = desired.Decision 88 | } 89 | 90 | if desired.Precedence != nil { 91 | if existing.Precedence != *desired.Precedence { 92 | hasChanged = true 93 | existing.Precedence = *desired.Precedence 94 | } 95 | } 96 | 97 | if !shared.StringSlicesMatch(shared.InterfaceArrayToStringArray(existing.Include), desired.Include) { 98 | hasChanged = true 99 | existing.Include = shared.StringArrayToInterfaceArray(desired.Include) 100 | } 101 | 102 | if !shared.StringSlicesMatch(shared.InterfaceArrayToStringArray(existing.Exclude), desired.Exclude) { 103 | hasChanged = true 104 | existing.Exclude = shared.StringArrayToInterfaceArray(desired.Exclude) 105 | } 106 | 107 | if !shared.StringSlicesMatch(shared.InterfaceArrayToStringArray(existing.Require), desired.Require) { 108 | hasChanged = true 109 | existing.Require = shared.StringArrayToInterfaceArray(desired.Require) 110 | } 111 | 112 | if !hasChanged { 113 | return nil, nil 114 | } 115 | 116 | return &existing, nil 117 | } 118 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | SHELL := /bin/bash 3 | VERSION ?=`git describe --tags` 4 | DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` 5 | VERSION_PACKAGE = github.com/replicatedhq/kubeflare/pkg/version 6 | GIT_TREE = $(shell git rev-parse --is-inside-work-tree 2>/dev/null) 7 | IMAGE_TAG ?= dev 8 | ifneq "$(GIT_TREE)" "" 9 | define GIT_UPDATE_INDEX_CMD 10 | git update-index --assume-unchanged 11 | endef 12 | define GIT_SHA 13 | `git rev-parse HEAD` 14 | endef 15 | else 16 | define GIT_UPDATE_INDEX_CMD 17 | echo "Not a git repo, skipping git update-index" 18 | endef 19 | define GIT_SHA 20 | "" 21 | endef 22 | endif 23 | 24 | define LDFLAGS 25 | -ldflags "\ 26 | -X ${VERSION_PACKAGE}.version=${VERSION} \ 27 | -X ${VERSION_PACKAGE}.gitSHA=${GIT_SHA} \ 28 | -X ${VERSION_PACKAGE}.buildTime=${DATE} \ 29 | " 30 | endef 31 | 32 | export GO111MODULE=on 33 | # export GOPROXY=https://proxy.golang.org 34 | 35 | all: generate fmt vet manifests kubeflare 36 | 37 | .PHONY: clean-and-tidy 38 | clean-and-tidy: 39 | @go clean -modcache ||: 40 | @go mod tidy ||: 41 | 42 | .PHONY: integration 43 | integration: integration-bin 44 | make -C integration run 45 | 46 | .PHONY: integration-bin 47 | integration-bin: generate fmt vet manifests 48 | go build \ 49 | ${LDFLAGS} \ 50 | -i \ 51 | -o bin/integration \ 52 | ./cmd/integration 53 | 54 | .PHONY: test 55 | test: generate fmt vet manifests 56 | go test ./pkg/... ./cmd/... -coverprofile cover.out 57 | 58 | .PHONY: kubeflare 59 | kubeflare: generate fmt vet bin/kubeflare 60 | 61 | .PHONY: bin/kubeflare 62 | bin/kubeflare: 63 | go build \ 64 | ${LDFLAGS} \ 65 | -i \ 66 | -o bin/kubeflare \ 67 | ./cmd/kubeflare 68 | 69 | .PHONY: run 70 | run: generate fmt vet bin/kubeflare 71 | ./bin/kubeflare run \ 72 | --log-level debug 73 | 74 | .PHONY: install 75 | install: manifests generate dev 76 | kubectl apply -f config/crds/v1 77 | 78 | .PHONY: deploy 79 | deploy: manifests 80 | kubectl apply -f config/crds/v1 81 | kustomize build config/default | kubectl apply -f - 82 | 83 | .PHONY: manifests 84 | manifests: controller-gen 85 | $(CONTROLLER_GEN) \ 86 | rbac:roleName=manager-role webhook \ 87 | crd:crdVersions=v1beta1 \ 88 | output:crd:artifacts:config=config/crds/v1beta1 \ 89 | paths="./..." 90 | $(CONTROLLER_GEN) \ 91 | rbac:roleName=manager-role webhook \ 92 | crd:crdVersions=v1 \ 93 | output:crd:artifacts:config=config/crds/v1 \ 94 | paths="./..." 95 | 96 | .PHONY: fmt 97 | fmt: 98 | go fmt ./pkg/... ./cmd/... 99 | 100 | .PHONY: vet 101 | vet: 102 | go vet ./pkg/... ./cmd/... 103 | 104 | .PHONY: generate 105 | generate: controller-gen client-gen 106 | $(CONTROLLER_GEN) object:headerFile=./hack/boilerplate.go.txt paths=./pkg/apis/... 107 | $(CLIENT_GEN) \ 108 | --output-package=github.com/replicatedhq/kubeflare/pkg/client \ 109 | --clientset-name kubeflareclientset \ 110 | --input-base github.com/replicatedhq/kubeflare/pkg/apis \ 111 | --input crds/v1alpha1 \ 112 | -h ./hack/boilerplate.go.txt 113 | 114 | .PHONY: dev 115 | dev: kubeflare 116 | docker build -t kubeflare/kubeflare-manager -f ./Dockerfile.manager . 117 | docker tag kubeflare/kubeflare-manager localhost:32000/kubeflare/kubeflare-manager:latest 118 | docker push localhost:32000/kubeflare/kubeflare-manager:latest 119 | 120 | .PHONY: image 121 | image: kubeflare 122 | docker build -t kubeflare/kubeflare-manager:$(IMAGE_TAG) -f ./Dockerfile.manager . 123 | 124 | .PHONY: contoller-gen 125 | controller-gen: 126 | ifeq (, $(shell which controller-gen)) 127 | go get sigs.k8s.io/controller-tools/cmd/controller-gen@v0.4.1 128 | CONTROLLER_GEN=$(shell go env GOPATH)/bin/controller-gen 129 | else 130 | CONTROLLER_GEN=$(shell which controller-gen) 131 | endif 132 | 133 | .PHONY: client-gen 134 | client-gen: 135 | ifeq (, $(shell which client-gen)) 136 | go get k8s.io/code-generator/cmd/client-gen@kubernetes-1.19.16 137 | CLIENT_GEN=$(shell go env GOPATH)/bin/client-gen 138 | else 139 | CLIENT_GEN=$(shell which client-gen) 140 | endif 141 | -------------------------------------------------------------------------------- /pkg/client/kubeflareclientset/typed/crds/v1alpha1/crds_client.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Replicated, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | package v1alpha1 20 | 21 | import ( 22 | v1alpha1 "github.com/replicatedhq/kubeflare/pkg/apis/crds/v1alpha1" 23 | "github.com/replicatedhq/kubeflare/pkg/client/kubeflareclientset/scheme" 24 | rest "k8s.io/client-go/rest" 25 | ) 26 | 27 | type CrdsV1alpha1Interface interface { 28 | RESTClient() rest.Interface 29 | APITokensGetter 30 | AccessApplicationsGetter 31 | DNSRecordsGetter 32 | PageRulesGetter 33 | WebApplicationFirewallRulesGetter 34 | WorkerRoutesGetter 35 | ZonesGetter 36 | } 37 | 38 | // CrdsV1alpha1Client is used to interact with features provided by the crds.kubeflare.io group. 39 | type CrdsV1alpha1Client struct { 40 | restClient rest.Interface 41 | } 42 | 43 | func (c *CrdsV1alpha1Client) APITokens(namespace string) APITokenInterface { 44 | return newAPITokens(c, namespace) 45 | } 46 | 47 | func (c *CrdsV1alpha1Client) AccessApplications(namespace string) AccessApplicationInterface { 48 | return newAccessApplications(c, namespace) 49 | } 50 | 51 | func (c *CrdsV1alpha1Client) DNSRecords(namespace string) DNSRecordInterface { 52 | return newDNSRecords(c, namespace) 53 | } 54 | 55 | func (c *CrdsV1alpha1Client) PageRules(namespace string) PageRuleInterface { 56 | return newPageRules(c, namespace) 57 | } 58 | 59 | func (c *CrdsV1alpha1Client) WebApplicationFirewallRules(namespace string) WebApplicationFirewallRuleInterface { 60 | return newWebApplicationFirewallRules(c, namespace) 61 | } 62 | 63 | func (c *CrdsV1alpha1Client) WorkerRoutes(namespace string) WorkerRouteInterface { 64 | return newWorkerRoutes(c, namespace) 65 | } 66 | 67 | func (c *CrdsV1alpha1Client) Zones(namespace string) ZoneInterface { 68 | return newZones(c, namespace) 69 | } 70 | 71 | // NewForConfig creates a new CrdsV1alpha1Client for the given config. 72 | func NewForConfig(c *rest.Config) (*CrdsV1alpha1Client, error) { 73 | config := *c 74 | if err := setConfigDefaults(&config); err != nil { 75 | return nil, err 76 | } 77 | client, err := rest.RESTClientFor(&config) 78 | if err != nil { 79 | return nil, err 80 | } 81 | return &CrdsV1alpha1Client{client}, nil 82 | } 83 | 84 | // NewForConfigOrDie creates a new CrdsV1alpha1Client for the given config and 85 | // panics if there is an error in the config. 86 | func NewForConfigOrDie(c *rest.Config) *CrdsV1alpha1Client { 87 | client, err := NewForConfig(c) 88 | if err != nil { 89 | panic(err) 90 | } 91 | return client 92 | } 93 | 94 | // New creates a new CrdsV1alpha1Client for the given RESTClient. 95 | func New(c rest.Interface) *CrdsV1alpha1Client { 96 | return &CrdsV1alpha1Client{c} 97 | } 98 | 99 | func setConfigDefaults(config *rest.Config) error { 100 | gv := v1alpha1.SchemeGroupVersion 101 | config.GroupVersion = &gv 102 | config.APIPath = "/apis" 103 | config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() 104 | 105 | if config.UserAgent == "" { 106 | config.UserAgent = rest.DefaultKubernetesUserAgent() 107 | } 108 | 109 | return nil 110 | } 111 | 112 | // RESTClient returns a RESTClient that is used to communicate 113 | // with API server by this client implementation. 114 | func (c *CrdsV1alpha1Client) RESTClient() rest.Interface { 115 | if c == nil { 116 | return nil 117 | } 118 | return c.restClient 119 | } 120 | -------------------------------------------------------------------------------- /config/crds/v1beta1/crds.kubeflare.io_pagerules.yaml: -------------------------------------------------------------------------------- 1 | 2 | --- 3 | apiVersion: apiextensions.k8s.io/v1beta1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | annotations: 7 | controller-gen.kubebuilder.io/version: v0.4.1 8 | creationTimestamp: null 9 | name: pagerules.crds.kubeflare.io 10 | spec: 11 | group: crds.kubeflare.io 12 | names: 13 | kind: PageRule 14 | listKind: PageRuleList 15 | plural: pagerules 16 | singular: pagerule 17 | scope: Namespaced 18 | subresources: 19 | status: {} 20 | validation: 21 | openAPIV3Schema: 22 | description: PageRule is the Schema for the pagerules API 23 | properties: 24 | apiVersion: 25 | description: 'APIVersion defines the versioned schema of this representation 26 | of an object. Servers should convert recognized schemas to the latest 27 | internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' 28 | type: string 29 | kind: 30 | description: 'Kind is a string value representing the REST resource this 31 | object represents. Servers may infer this from the endpoint the client 32 | submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' 33 | type: string 34 | metadata: 35 | type: object 36 | spec: 37 | description: PageRuleSpec defines the desired state of PageRule 38 | properties: 39 | pageRule: 40 | properties: 41 | alwaysUseHttps: 42 | type: object 43 | autoMinify: 44 | properties: 45 | css: 46 | type: boolean 47 | html: 48 | type: boolean 49 | js: 50 | type: boolean 51 | required: 52 | - css 53 | - html 54 | - js 55 | type: object 56 | enabled: 57 | type: boolean 58 | forwardingUrl: 59 | properties: 60 | redirectUrl: 61 | type: string 62 | statusCode: 63 | type: integer 64 | required: 65 | - redirectUrl 66 | - statusCode 67 | type: object 68 | hostHeaderOverride: 69 | properties: 70 | value: 71 | type: string 72 | required: 73 | - value 74 | type: object 75 | priority: 76 | type: integer 77 | requestUrl: 78 | type: string 79 | resolveOverride: 80 | properties: 81 | value: 82 | type: string 83 | required: 84 | - value 85 | type: object 86 | required: 87 | - requestUrl 88 | type: object 89 | zone: 90 | type: string 91 | required: 92 | - zone 93 | type: object 94 | status: 95 | description: PageRuleStatus defines the observed state of PageRule We are 96 | storing the requested priority here because the priority is different 97 | on cloudflare side and hence we cannot depend on the one from its API 98 | to detect changes to the spec 99 | properties: 100 | id: 101 | type: string 102 | lastAppliedPriority: 103 | type: integer 104 | type: object 105 | type: object 106 | version: v1alpha1 107 | versions: 108 | - name: v1alpha1 109 | served: true 110 | storage: true 111 | status: 112 | acceptedNames: 113 | kind: "" 114 | plural: "" 115 | conditions: [] 116 | storedVersions: [] 117 | -------------------------------------------------------------------------------- /config/crds/v1/crds.kubeflare.io_pagerules.yaml: -------------------------------------------------------------------------------- 1 | 2 | --- 3 | apiVersion: apiextensions.k8s.io/v1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | annotations: 7 | controller-gen.kubebuilder.io/version: v0.4.1 8 | creationTimestamp: null 9 | name: pagerules.crds.kubeflare.io 10 | spec: 11 | group: crds.kubeflare.io 12 | names: 13 | kind: PageRule 14 | listKind: PageRuleList 15 | plural: pagerules 16 | singular: pagerule 17 | scope: Namespaced 18 | versions: 19 | - name: v1alpha1 20 | schema: 21 | openAPIV3Schema: 22 | description: PageRule is the Schema for the pagerules API 23 | properties: 24 | apiVersion: 25 | description: 'APIVersion defines the versioned schema of this representation 26 | of an object. Servers should convert recognized schemas to the latest 27 | internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' 28 | type: string 29 | kind: 30 | description: 'Kind is a string value representing the REST resource this 31 | object represents. Servers may infer this from the endpoint the client 32 | submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' 33 | type: string 34 | metadata: 35 | type: object 36 | spec: 37 | description: PageRuleSpec defines the desired state of PageRule 38 | properties: 39 | pageRule: 40 | properties: 41 | alwaysUseHttps: 42 | type: object 43 | autoMinify: 44 | properties: 45 | css: 46 | type: boolean 47 | html: 48 | type: boolean 49 | js: 50 | type: boolean 51 | required: 52 | - css 53 | - html 54 | - js 55 | type: object 56 | enabled: 57 | type: boolean 58 | forwardingUrl: 59 | properties: 60 | redirectUrl: 61 | type: string 62 | statusCode: 63 | type: integer 64 | required: 65 | - redirectUrl 66 | - statusCode 67 | type: object 68 | hostHeaderOverride: 69 | properties: 70 | value: 71 | type: string 72 | required: 73 | - value 74 | type: object 75 | priority: 76 | type: integer 77 | requestUrl: 78 | type: string 79 | resolveOverride: 80 | properties: 81 | value: 82 | type: string 83 | required: 84 | - value 85 | type: object 86 | required: 87 | - requestUrl 88 | type: object 89 | zone: 90 | type: string 91 | required: 92 | - zone 93 | type: object 94 | status: 95 | description: PageRuleStatus defines the observed state of PageRule We 96 | are storing the requested priority here because the priority is different 97 | on cloudflare side and hence we cannot depend on the one from its API 98 | to detect changes to the spec 99 | properties: 100 | id: 101 | type: string 102 | lastAppliedPriority: 103 | type: integer 104 | type: object 105 | type: object 106 | served: true 107 | storage: true 108 | subresources: 109 | status: {} 110 | status: 111 | acceptedNames: 112 | kind: "" 113 | plural: "" 114 | conditions: [] 115 | storedVersions: [] 116 | -------------------------------------------------------------------------------- /pkg/cli/kubeflarecli/manager.go: -------------------------------------------------------------------------------- 1 | package kubeflarecli 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/replicatedhq/kubeflare/pkg/apis" 7 | accessapplicationcontroller "github.com/replicatedhq/kubeflare/pkg/controller/accessapplication" 8 | apitokencontroller "github.com/replicatedhq/kubeflare/pkg/controller/apitoken" 9 | dnsrecordcontroller "github.com/replicatedhq/kubeflare/pkg/controller/dnsrecord" 10 | pagerulecontroller "github.com/replicatedhq/kubeflare/pkg/controller/pagerule" 11 | wafrulecontroller "github.com/replicatedhq/kubeflare/pkg/controller/webapplicationfirewallrule" 12 | workerroutecontroller "github.com/replicatedhq/kubeflare/pkg/controller/workerroute" 13 | zonecontroller "github.com/replicatedhq/kubeflare/pkg/controller/zone" 14 | "github.com/replicatedhq/kubeflare/pkg/logger" 15 | "github.com/replicatedhq/kubeflare/pkg/version" 16 | "github.com/replicatedhq/kubeflare/pkg/webhook" 17 | "github.com/spf13/cobra" 18 | "github.com/spf13/viper" 19 | _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" 20 | "sigs.k8s.io/controller-runtime/pkg/client/config" 21 | "sigs.k8s.io/controller-runtime/pkg/manager" 22 | "sigs.k8s.io/controller-runtime/pkg/manager/signals" 23 | ) 24 | 25 | func ManagerCmd() *cobra.Command { 26 | cmd := &cobra.Command{ 27 | Use: "manager", 28 | Short: "runs the kubeflare manager (in cluster controller)", 29 | Long: `...`, 30 | SilenceErrors: true, 31 | SilenceUsage: true, 32 | PreRun: func(cmd *cobra.Command, args []string) { 33 | viper.BindPFlags(cmd.Flags()) 34 | }, 35 | RunE: func(cmd *cobra.Command, args []string) error { 36 | logger.Infof("Starting kubeflare manager version %+v", version.GetBuild()) 37 | 38 | v := viper.GetViper() 39 | 40 | if v.GetString("log-level") == "debug" { 41 | logger.Info("setting log level to debug") 42 | logger.SetDebug() 43 | } 44 | 45 | // Get a config to talk to the apiserver 46 | cfg, err := config.GetConfig() 47 | if err != nil { 48 | logger.Error(err) 49 | os.Exit(1) 50 | } 51 | 52 | // Create a new Cmd to provide shared dependencies and start components 53 | options := manager.Options{ 54 | MetricsBindAddress: v.GetString("metrics-addr"), 55 | LeaderElection: v.GetBool("leader-elect"), 56 | LeaderElectionID: "leaderelection.kubeflare.io", 57 | } 58 | 59 | mgr, err := manager.New(cfg, options) 60 | if err != nil { 61 | logger.Error(err) 62 | os.Exit(1) 63 | } 64 | 65 | // Setup Scheme for all resources 66 | if err := apis.AddToScheme(mgr.GetScheme()); err != nil { 67 | logger.Error(err) 68 | os.Exit(1) 69 | } 70 | 71 | protectAPIToken := v.GetBool("protect-apitoken") 72 | if protectAPIToken { 73 | err = apitokencontroller.Add(mgr) 74 | 75 | if err != nil { 76 | logger.Error(err) 77 | os.Exit(1) 78 | } 79 | } 80 | 81 | if err := zonecontroller.Add(mgr, protectAPIToken); err != nil { 82 | logger.Error(err) 83 | os.Exit(1) 84 | } 85 | 86 | if err := dnsrecordcontroller.Add(mgr); err != nil { 87 | logger.Error(err) 88 | os.Exit(1) 89 | } 90 | 91 | if err := pagerulecontroller.Add(mgr); err != nil { 92 | logger.Error(err) 93 | os.Exit(1) 94 | } 95 | 96 | if err := accessapplicationcontroller.Add(mgr); err != nil { 97 | logger.Error(err) 98 | os.Exit(1) 99 | } 100 | 101 | if err := wafrulecontroller.Add(mgr); err != nil { 102 | logger.Error(err) 103 | os.Exit(1) 104 | } 105 | 106 | if err := workerroutecontroller.Add(mgr); err != nil { 107 | logger.Error(err) 108 | os.Exit(1) 109 | } 110 | 111 | if err := webhook.AddToManager(mgr); err != nil { 112 | logger.Error(err) 113 | os.Exit(1) 114 | } 115 | 116 | // Start the Cmd 117 | if err := mgr.Start(signals.SetupSignalHandler()); err != nil { 118 | logger.Error(err) 119 | os.Exit(1) 120 | } 121 | 122 | return nil 123 | }, 124 | } 125 | 126 | cmd.Flags().String("metrics-addr", ":8088", "The address the metric endpoint binds to.") 127 | cmd.Flags().Bool("leader-elect", true, "Enable leader election for controller manager. "+ 128 | "Enabling this will ensure there is only one active controller manager.") 129 | cmd.Flags().Bool("protect-apitoken", false, "Protect APIToken from deletion if it is referenced by other managed resources.") 130 | 131 | return cmd 132 | } 133 | -------------------------------------------------------------------------------- /pkg/controller/dnsrecord/dnsrecord_controller.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Replicated, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package dnsrecord 18 | 19 | import ( 20 | "context" 21 | "time" 22 | 23 | "github.com/pkg/errors" 24 | crdsv1alpha1 "github.com/replicatedhq/kubeflare/pkg/apis/crds/v1alpha1" 25 | "github.com/replicatedhq/kubeflare/pkg/controller/shared" 26 | "github.com/replicatedhq/kubeflare/pkg/logger" 27 | "k8s.io/apimachinery/pkg/runtime" 28 | kubeinformers "k8s.io/client-go/informers" 29 | "k8s.io/client-go/kubernetes" 30 | "sigs.k8s.io/controller-runtime/pkg/client" 31 | "sigs.k8s.io/controller-runtime/pkg/controller" 32 | "sigs.k8s.io/controller-runtime/pkg/handler" 33 | "sigs.k8s.io/controller-runtime/pkg/manager" 34 | "sigs.k8s.io/controller-runtime/pkg/reconcile" 35 | "sigs.k8s.io/controller-runtime/pkg/source" 36 | ) 37 | 38 | // Add creates a new DNSRecord Controller and adds it to the Manager with default RBAC. The Manager will set fields on the Controller 39 | // and Start it when the Manager is Started. 40 | func Add(mgr manager.Manager) error { 41 | return add(mgr, newReconciler(mgr)) 42 | } 43 | 44 | // newReconciler returns a new reconcile.Reconciler 45 | func newReconciler(mgr manager.Manager) reconcile.Reconciler { 46 | return &ReconcileDNSRecord{ 47 | Client: mgr.GetClient(), 48 | scheme: mgr.GetScheme(), 49 | } 50 | } 51 | 52 | // add adds a new Controller to mgr with r as the reconcile.Reconciler 53 | func add(mgr manager.Manager, r reconcile.Reconciler) error { 54 | // Create a new controller 55 | c, err := controller.New("dnsrecord-controller", mgr, controller.Options{Reconciler: r}) 56 | if err != nil { 57 | return err 58 | } 59 | 60 | // Watch for changes to DNSRecord 61 | err = c.Watch(&source.Kind{ 62 | Type: &crdsv1alpha1.DNSRecord{}, 63 | }, &handler.EnqueueRequestForObject{}) 64 | if err != nil { 65 | return errors.Wrap(err, "failed to start watch on dnsrecords") 66 | } 67 | 68 | generatedClient := kubernetes.NewForConfigOrDie(mgr.GetConfig()) 69 | generatedInformers := kubeinformers.NewSharedInformerFactory(generatedClient, time.Minute) 70 | err = mgr.Add(manager.RunnableFunc(func(s <-chan struct{}) error { 71 | generatedInformers.Start(s) 72 | <-s 73 | return nil 74 | })) 75 | if err != nil { 76 | return err 77 | } 78 | 79 | return nil 80 | } 81 | 82 | var _ reconcile.Reconciler = &ReconcileDNSRecord{} 83 | 84 | // ReconcileDNSRecord reconciles a DNSRecord object 85 | type ReconcileDNSRecord struct { 86 | client.Client 87 | scheme *runtime.Scheme 88 | } 89 | 90 | // Reconcile reads that state of the cluster for a ReconcileDNSRecord object and makes changes based on the state read 91 | // and what is in the Zone.Spec 92 | // +kubebuilder:rbac:groups=crds.kubeflare.io,resources=dnsrecords,verbs=get;list;watch;create;update;patch;delete 93 | // +kubebuilder:rbac:groups=crds.kubeflare.io,resources=dnsrecords/status,verbs=get;update;patch 94 | func (r *ReconcileDNSRecord) Reconcile(request reconcile.Request) (reconcile.Result, error) { 95 | // This reconcile loop will be called for all ReconcileDNSRecord objects 96 | // because of the informer that we have set up 97 | ctx := context.Background() 98 | instance := crdsv1alpha1.DNSRecord{} 99 | err := r.Get(ctx, request.NamespacedName, &instance) 100 | if err != nil { 101 | logger.Error(err) 102 | return reconcile.Result{}, err 103 | } 104 | 105 | zone, err := shared.GetZone(ctx, instance.Namespace, instance.Spec.Zone) 106 | if err != nil { 107 | logger.Error(err) 108 | return reconcile.Result{}, err 109 | } 110 | 111 | cf, err := shared.GetCloudflareAPI(ctx, instance.Namespace, zone.Spec.APIToken) 112 | if err != nil { 113 | logger.Error(err) 114 | return reconcile.Result{}, err 115 | } 116 | 117 | if err := ReconcileDNSRecordInstances(ctx, instance, zone, cf); err != nil { 118 | logger.Error(err) 119 | return reconcile.Result{}, err 120 | } 121 | 122 | return reconcile.Result{}, nil 123 | } 124 | -------------------------------------------------------------------------------- /pkg/controller/dnsrecord/dns_records.go: -------------------------------------------------------------------------------- 1 | package dnsrecord 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/cloudflare/cloudflare-go" 7 | "github.com/pkg/errors" 8 | crdsv1alpha1 "github.com/replicatedhq/kubeflare/pkg/apis/crds/v1alpha1" 9 | "github.com/replicatedhq/kubeflare/pkg/logger" 10 | ) 11 | 12 | func ReconcileDNSRecordInstances(ctx context.Context, instance crdsv1alpha1.DNSRecord, zone *crdsv1alpha1.Zone, cf *cloudflare.API) error { 13 | logger.Debug("reconcileDNSRecords for zone") 14 | 15 | zoneID, err := cf.ZoneIDByName(zone.Name) 16 | if err != nil { 17 | return errors.Wrap(err, "failed to get zone id") 18 | } 19 | 20 | existingRecords, err := cf.DNSRecords(ctx, zoneID, cloudflare.DNSRecord{}) 21 | if err != nil { 22 | return errors.Wrap(err, "failed to list dns records") 23 | } 24 | 25 | desiredRecords := []*crdsv1alpha1.Record{} 26 | if instance.Spec.Record != nil { 27 | desiredRecords = append(desiredRecords, instance.Spec.Record) 28 | } 29 | if instance.Spec.Records != nil { 30 | desiredRecords = append(desiredRecords, instance.Spec.Records...) 31 | } 32 | 33 | recordsToCreate := []cloudflare.DNSRecord{} 34 | recordsToUpdate := []cloudflare.DNSRecord{} 35 | // recordsToDelete := []cloudflare.DNSRecord{} 36 | 37 | for _, existingRecord := range existingRecords { 38 | found := false 39 | for _, desiredRecord := range desiredRecords { 40 | if desiredRecord.Name == existingRecord.Name && desiredRecord.Type == existingRecord.Type { 41 | found = true 42 | isChanged := false 43 | 44 | if desiredRecord.Content != existingRecord.Content { 45 | isChanged = true 46 | existingRecord.Content = desiredRecord.Content 47 | } 48 | desiredTTL := 1 49 | if desiredRecord.TTL != nil { 50 | desiredTTL = *desiredRecord.TTL 51 | } 52 | if desiredTTL != existingRecord.TTL { 53 | isChanged = true 54 | existingRecord.TTL = desiredTTL 55 | } 56 | if desiredRecord.Priority != nil { 57 | if *desiredRecord.Priority != int(*existingRecord.Priority) { 58 | isChanged = true 59 | priority := uint16(*desiredRecord.Priority) 60 | existingRecord.Priority = &priority 61 | } 62 | } 63 | if desiredRecord.Proxied != nil { 64 | if *desiredRecord.Proxied != *existingRecord.Proxied { 65 | isChanged = true 66 | existingRecord.Proxied = desiredRecord.Proxied 67 | } 68 | } 69 | 70 | if isChanged { 71 | recordsToUpdate = append(recordsToUpdate, existingRecord) 72 | } 73 | } 74 | } 75 | if !found { 76 | // TODO this feels dangerous, how can we opt-in to delete somehow to avoid erasing all records 77 | // recordsToDelete = append(recordsToDelete, existingRecord) 78 | } 79 | } 80 | 81 | for _, desiredRecord := range desiredRecords { 82 | found := false 83 | for _, existingRecord := range existingRecords { 84 | if existingRecord.Type == desiredRecord.Type && existingRecord.Name == desiredRecord.Name { 85 | found = true 86 | goto Found 87 | } 88 | } 89 | Found: 90 | if !found { 91 | recordToCreate := cloudflare.DNSRecord{ 92 | Type: desiredRecord.Type, 93 | Name: desiredRecord.Name, 94 | Content: desiredRecord.Content, 95 | } 96 | if desiredRecord.TTL != nil { 97 | recordToCreate.TTL = *desiredRecord.TTL 98 | } else { 99 | recordToCreate.TTL = 1 100 | } 101 | 102 | if desiredRecord.Priority != nil { 103 | priority := uint16(*desiredRecord.Priority) 104 | recordToCreate.Priority = &priority 105 | } 106 | if desiredRecord.Proxied != nil { 107 | recordToCreate.Proxied = desiredRecord.Proxied 108 | } 109 | recordsToCreate = append(recordsToCreate, recordToCreate) 110 | } 111 | } 112 | 113 | for _, recordToCreate := range recordsToCreate { 114 | response, err := cf.CreateDNSRecord(ctx, zoneID, recordToCreate) 115 | if err != nil { 116 | return errors.Wrap(err, "failed to create dns record") 117 | } 118 | 119 | if !response.Success { 120 | return errors.New("non success when creating dns record") 121 | } 122 | } 123 | 124 | for _, recordToUpdate := range recordsToUpdate { 125 | rr := cloudflare.DNSRecord{ 126 | Type: recordToUpdate.Type, 127 | Name: recordToUpdate.Name, 128 | Content: recordToUpdate.Content, 129 | TTL: recordToUpdate.TTL, 130 | Proxied: recordToUpdate.Proxied, 131 | } 132 | 133 | err := cf.UpdateDNSRecord(ctx, zoneID, recordToUpdate.ID, rr) 134 | if err != nil { 135 | return errors.Wrap(err, "failed to update dns record") 136 | } 137 | } 138 | 139 | return nil 140 | } 141 | -------------------------------------------------------------------------------- /pkg/controller/webapplicationfirewallrule/webapplicationfirewallrule_controller.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Replicated, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package webapplicationfirewallrule 18 | 19 | import ( 20 | "context" 21 | "time" 22 | 23 | crdsv1alpha1 "github.com/replicatedhq/kubeflare/pkg/apis/crds/v1alpha1" 24 | "github.com/replicatedhq/kubeflare/pkg/controller/shared" 25 | "github.com/replicatedhq/kubeflare/pkg/logger" 26 | "k8s.io/apimachinery/pkg/runtime" 27 | kubeinformers "k8s.io/client-go/informers" 28 | "k8s.io/client-go/kubernetes" 29 | "sigs.k8s.io/controller-runtime/pkg/client" 30 | "sigs.k8s.io/controller-runtime/pkg/controller" 31 | "sigs.k8s.io/controller-runtime/pkg/handler" 32 | "sigs.k8s.io/controller-runtime/pkg/manager" 33 | "sigs.k8s.io/controller-runtime/pkg/reconcile" 34 | "sigs.k8s.io/controller-runtime/pkg/source" 35 | ) 36 | 37 | // Add creates a new WebApplicationFirewallRule Controller and adds it to the Manager with default RBAC. The Manager will set fields on the Controller 38 | // and Start it when the Manager is Started. 39 | func Add(mgr manager.Manager) error { 40 | return add(mgr, newReconciler(mgr)) 41 | } 42 | 43 | // newReconciler returns a new reconcile.Reconciler 44 | func newReconciler(mgr manager.Manager) reconcile.Reconciler { 45 | return &ReconcileWebApplicationFirewallRule{Client: mgr.GetClient(), scheme: mgr.GetScheme()} 46 | } 47 | 48 | // add adds a new Controller to mgr with r as the reconcile.Reconciler 49 | func add(mgr manager.Manager, r reconcile.Reconciler) error { 50 | // Create a new controller 51 | c, err := controller.New("webapplicationfirewallrule-controller", mgr, controller.Options{Reconciler: r}) 52 | if err != nil { 53 | return err 54 | } 55 | 56 | // Watch for changes to WebApplicationFirewallRule 57 | err = c.Watch(&source.Kind{Type: &crdsv1alpha1.WebApplicationFirewallRule{}}, &handler.EnqueueRequestForObject{}) 58 | if err != nil { 59 | return err 60 | } 61 | 62 | generatedClient := kubernetes.NewForConfigOrDie(mgr.GetConfig()) 63 | generatedInformers := kubeinformers.NewSharedInformerFactory(generatedClient, time.Minute) 64 | err = mgr.Add(manager.RunnableFunc(func(s <-chan struct{}) error { 65 | generatedInformers.Start(s) 66 | <-s 67 | return nil 68 | })) 69 | if err != nil { 70 | return err 71 | } 72 | 73 | return nil 74 | } 75 | 76 | var _ reconcile.Reconciler = &ReconcileWebApplicationFirewallRule{} 77 | 78 | // ReconcileWebApplicationFirewallRule reconciles a WebApplicationFirewallRule object 79 | type ReconcileWebApplicationFirewallRule struct { 80 | client.Client 81 | scheme *runtime.Scheme 82 | } 83 | 84 | // Reconcile reads that state of the cluster for a WebApplicationFirewallRule object and makes changes based on the state read 85 | // and what is in the WebApplicationFirewallRule.Spec 86 | // Automatically generate RBAC rules to allow the Controller to read and write Deployments 87 | // +kubebuilder:rbac:groups=crds.kubeflare.io,resources=webapplicationfirewallrules,verbs=get;list;watch;create;update;patch;delete 88 | // +kubebuilder:rbac:groups=crds.kubeflare.io,resources=webapplicationfirewallrules/status,verbs=get;update;patch 89 | func (r *ReconcileWebApplicationFirewallRule) Reconcile(request reconcile.Request) (reconcile.Result, error) { 90 | // Fetch the WebApplicationFirewallRule instance 91 | ctx := context.Background() 92 | instance := crdsv1alpha1.WebApplicationFirewallRule{} 93 | err := r.Get(ctx, request.NamespacedName, &instance) 94 | if err != nil { 95 | logger.Error(err) 96 | return reconcile.Result{}, err 97 | } 98 | 99 | zone, err := shared.GetZone(ctx, instance.Namespace, instance.Spec.Zone) 100 | if err != nil { 101 | logger.Error(err) 102 | return reconcile.Result{}, err 103 | } 104 | 105 | cf, err := shared.GetCloudflareAPI(ctx, instance.Namespace, zone.Spec.APIToken) 106 | if err != nil { 107 | logger.Error(err) 108 | return reconcile.Result{}, err 109 | } 110 | 111 | if err := ReconcileWAFRuleInstances(ctx, instance, zone, cf); err != nil { 112 | logger.Error(err) 113 | return reconcile.Result{}, err 114 | } 115 | 116 | return reconcile.Result{}, nil 117 | } 118 | -------------------------------------------------------------------------------- /pkg/controller/pagerule/pagerule_controller.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Replicated, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package pagerule 18 | 19 | import ( 20 | "context" 21 | "go.uber.org/zap" 22 | "time" 23 | 24 | "github.com/pkg/errors" 25 | crdsv1alpha1 "github.com/replicatedhq/kubeflare/pkg/apis/crds/v1alpha1" 26 | "github.com/replicatedhq/kubeflare/pkg/controller/shared" 27 | "github.com/replicatedhq/kubeflare/pkg/logger" 28 | apiErrors "k8s.io/apimachinery/pkg/api/errors" 29 | "k8s.io/apimachinery/pkg/runtime" 30 | kubeinformers "k8s.io/client-go/informers" 31 | "k8s.io/client-go/kubernetes" 32 | "sigs.k8s.io/controller-runtime/pkg/client" 33 | "sigs.k8s.io/controller-runtime/pkg/controller" 34 | "sigs.k8s.io/controller-runtime/pkg/handler" 35 | "sigs.k8s.io/controller-runtime/pkg/manager" 36 | "sigs.k8s.io/controller-runtime/pkg/reconcile" 37 | "sigs.k8s.io/controller-runtime/pkg/source" 38 | ) 39 | 40 | // Add creates a new PageRule Controller and adds it to the Manager with default RBAC. The Manager will set fields on the Controller 41 | // and Start it when the Manager is Started. 42 | func Add(mgr manager.Manager) error { 43 | return add(mgr, newReconciler(mgr)) 44 | } 45 | 46 | // newReconciler returns a new reconcile.Reconciler 47 | func newReconciler(mgr manager.Manager) reconcile.Reconciler { 48 | return &ReconcilePageRule{ 49 | Client: mgr.GetClient(), 50 | scheme: mgr.GetScheme(), 51 | } 52 | } 53 | 54 | // add adds a new Controller to mgr with r as the reconcile.Reconciler 55 | func add(mgr manager.Manager, r reconcile.Reconciler) error { 56 | // Create a new controller 57 | c, err := controller.New("pagerule-controller", mgr, controller.Options{Reconciler: r}) 58 | if err != nil { 59 | return err 60 | } 61 | 62 | // Watch for changes to PageRule 63 | err = c.Watch(&source.Kind{ 64 | Type: &crdsv1alpha1.PageRule{}, 65 | }, &handler.EnqueueRequestForObject{}) 66 | if err != nil { 67 | return errors.Wrap(err, "failed to start watch on pagerule") 68 | } 69 | 70 | generatedClient := kubernetes.NewForConfigOrDie(mgr.GetConfig()) 71 | generatedInformers := kubeinformers.NewSharedInformerFactory(generatedClient, time.Minute) 72 | err = mgr.Add(manager.RunnableFunc(func(s <-chan struct{}) error { 73 | generatedInformers.Start(s) 74 | <-s 75 | return nil 76 | })) 77 | if err != nil { 78 | return err 79 | } 80 | 81 | return nil 82 | } 83 | 84 | var _ reconcile.Reconciler = &ReconcilePageRule{} 85 | 86 | // ReconcilePageRule reconciles a PageRule object 87 | type ReconcilePageRule struct { 88 | client.Client 89 | scheme *runtime.Scheme 90 | } 91 | 92 | // Reconcile reads that state of the cluster for a ReconcilePageRule object and makes changes based on the state read 93 | // and what is in the Zone.Spec 94 | // +kubebuilder:rbac:groups=crds.kubeflare.io,resources=pagerules,verbs=get;list;watch;create;update;patch;delete 95 | // +kubebuilder:rbac:groups=crds.kubeflare.io,resources=pagerules/status,verbs=get;update;patch 96 | func (r *ReconcilePageRule) Reconcile(request reconcile.Request) (reconcile.Result, error) { 97 | // This reconcile loop will be called for all ReconcilePageRule objects 98 | // because of the informer that we have set up 99 | ctx := context.Background() 100 | instance := crdsv1alpha1.PageRule{} 101 | err := r.Get(ctx, request.NamespacedName, &instance) 102 | if err != nil { 103 | if apiErrors.IsNotFound(err) { 104 | logger.Debug("page rule already deleted", zap.String("name", request.Name)) 105 | return reconcile.Result{}, nil 106 | } 107 | 108 | logger.Error(err) 109 | return reconcile.Result{}, err 110 | } 111 | 112 | zone, err := shared.GetZone(ctx, instance.Namespace, instance.Spec.Zone) 113 | if err != nil { 114 | logger.Error(err) 115 | return reconcile.Result{}, err 116 | } 117 | 118 | cf, err := shared.GetCloudflareAPI(ctx, instance.Namespace, zone.Spec.APIToken) 119 | if err != nil { 120 | logger.Error(err) 121 | return reconcile.Result{}, err 122 | } 123 | 124 | if err := r.ReconcilePageRules(ctx, instance, zone, cf); err != nil { 125 | logger.Error(err) 126 | return reconcile.Result{}, err 127 | } 128 | 129 | return reconcile.Result{}, nil 130 | } 131 | -------------------------------------------------------------------------------- /pkg/cli/kubeflarecli/import.go: -------------------------------------------------------------------------------- 1 | package kubeflarecli 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io/ioutil" 7 | "os" 8 | "path/filepath" 9 | 10 | "github.com/cloudflare/cloudflare-go" 11 | "github.com/pkg/errors" 12 | kubeflarescheme "github.com/replicatedhq/kubeflare/pkg/client/kubeflareclientset/scheme" 13 | "github.com/replicatedhq/kubeflare/pkg/cloudflare/dns" 14 | "github.com/replicatedhq/kubeflare/pkg/cloudflare/pagerules" 15 | "github.com/replicatedhq/kubeflare/pkg/cloudflare/workerroute" 16 | "github.com/spf13/cobra" 17 | "github.com/spf13/viper" 18 | serializer "k8s.io/apimachinery/pkg/runtime/serializer/json" 19 | "k8s.io/client-go/kubernetes/scheme" 20 | ) 21 | 22 | func ImportCmd() *cobra.Command { 23 | cmd := &cobra.Command{ 24 | Use: "import", 25 | Short: "import existing settings from cloudflare into custom resources", 26 | Long: `...`, 27 | SilenceErrors: true, 28 | SilenceUsage: true, 29 | PreRun: func(cmd *cobra.Command, args []string) { 30 | viper.BindPFlags(cmd.Flags()) 31 | }, 32 | RunE: func(cmd *cobra.Command, args []string) error { 33 | v := viper.GetViper() 34 | 35 | _, err := os.Stat(v.GetString("output-dir")) 36 | if os.IsNotExist(err) { 37 | if err := os.MkdirAll(v.GetString("output-dir"), 0755); err != nil { 38 | return errors.Wrap(err, "mkdir") 39 | } 40 | } else if err != nil { 41 | return errors.Wrap(err, "stat") 42 | } 43 | 44 | cf, err := cloudflare.NewWithAPIToken(v.GetString("api-token")) 45 | if err != nil { 46 | return errors.Wrap(err, "create clouflare client") 47 | } 48 | 49 | zoneID, err := cf.ZoneIDByName(v.GetString("zone")) 50 | if err != nil { 51 | return errors.Wrap(err, "get zone id") 52 | } 53 | 54 | kubeflarescheme.AddToScheme(scheme.Scheme) 55 | s := serializer.NewYAMLSerializer(serializer.DefaultMetaFactory, scheme.Scheme, scheme.Scheme) 56 | 57 | if v.GetBool("dns-records") { 58 | dnsRecords, err := dns.FetchDNSRecordsForZone(v.GetString("api-token"), v.GetString("zone"), zoneID) 59 | if err != nil { 60 | return errors.Wrap(err, "fetch dns records") 61 | } 62 | 63 | for _, dnsRecord := range dnsRecords { 64 | buf := bytes.NewBuffer(nil) 65 | err := s.Encode(dnsRecord, buf) 66 | if err != nil { 67 | return errors.Wrap(err, "encode") 68 | } 69 | outputFile := filepath.Join(v.GetString("output-dir"), fmt.Sprintf("%s.yaml", dnsRecord.Name)) 70 | if err := ioutil.WriteFile(outputFile, buf.Bytes(), 0644); err != nil { 71 | return errors.Wrap(err, "write file") 72 | } 73 | } 74 | } 75 | 76 | if v.GetBool("page-rules") { 77 | pageRules, err := pagerules.FetchPageRulesForZone(v.GetString("api-token"), v.GetString("zone"), zoneID) 78 | if err != nil { 79 | return errors.Wrap(err, "fetch page rules") 80 | } 81 | 82 | for _, pageRule := range pageRules { 83 | buf := bytes.NewBuffer(nil) 84 | err := s.Encode(pageRule, buf) 85 | if err != nil { 86 | return errors.Wrap(err, "encode") 87 | } 88 | outputFile := filepath.Join(v.GetString("output-dir"), fmt.Sprintf("%s.yaml", pageRule.Name)) 89 | if err := ioutil.WriteFile(outputFile, buf.Bytes(), 0644); err != nil { 90 | return errors.Wrap(err, "write file") 91 | } 92 | } 93 | } 94 | 95 | if v.GetBool("worker-routes") { 96 | workerRoutes, err := workerroute.FetchWorkerRoutesForZone(v.GetString("api-token"), v.GetString("zone"), zoneID) 97 | if err != nil { 98 | return errors.Wrap(err, "fetch worker routes") 99 | } 100 | 101 | for _, workerRoute := range workerRoutes { 102 | buf := bytes.NewBuffer(nil) 103 | err := s.Encode(workerRoute, buf) 104 | if err != nil { 105 | return errors.Wrap(err, "encode") 106 | } 107 | outputFile := filepath.Join(v.GetString("output-dir"), fmt.Sprintf("%s.yaml", workerRoute.Name)) 108 | if err := ioutil.WriteFile(outputFile, buf.Bytes(), 0644); err != nil { 109 | return errors.Wrap(err, "write file") 110 | } 111 | } 112 | } 113 | 114 | return nil 115 | }, 116 | } 117 | 118 | cmd.Flags().String("api-token", "", "cloudflare api token") 119 | cmd.MarkFlagRequired("api-token") 120 | 121 | cmd.Flags().String("zone", "", "dns zone to import") 122 | cmd.MarkFlagRequired("zone") 123 | 124 | cmd.Flags().String("output-dir", filepath.Join(".", "imported"), "output dir to write files to") 125 | 126 | cmd.Flags().Bool("dns-records", true, "when set, import existing dns records from the zone") 127 | cmd.Flags().Bool("page-rules", true, "when set, import existing page rules from the zone") 128 | cmd.Flags().Bool("worker-routes", true, "when set, import existing worker routes from the zone") 129 | 130 | return cmd 131 | } 132 | -------------------------------------------------------------------------------- /config/crds/v1beta1/crds.kubeflare.io_accessapplications.yaml: -------------------------------------------------------------------------------- 1 | 2 | --- 3 | apiVersion: apiextensions.k8s.io/v1beta1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | annotations: 7 | controller-gen.kubebuilder.io/version: v0.4.1 8 | creationTimestamp: null 9 | name: accessapplications.crds.kubeflare.io 10 | spec: 11 | group: crds.kubeflare.io 12 | names: 13 | kind: AccessApplication 14 | listKind: AccessApplicationList 15 | plural: accessapplications 16 | singular: accessapplication 17 | scope: Namespaced 18 | subresources: 19 | status: {} 20 | validation: 21 | openAPIV3Schema: 22 | description: DNSRecord is the Schema for the accessapplication API 23 | properties: 24 | apiVersion: 25 | description: 'APIVersion defines the versioned schema of this representation 26 | of an object. Servers should convert recognized schemas to the latest 27 | internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' 28 | type: string 29 | kind: 30 | description: 'Kind is a string value representing the REST resource this 31 | object represents. Servers may infer this from the endpoint the client 32 | submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' 33 | type: string 34 | metadata: 35 | type: object 36 | spec: 37 | description: AccessApplicationSpec defines the desired state of AccessApplication 38 | properties: 39 | accessPolicies: 40 | items: 41 | properties: 42 | descision: 43 | type: string 44 | exclude: 45 | items: 46 | type: string 47 | type: array 48 | include: 49 | items: 50 | type: string 51 | type: array 52 | name: 53 | type: string 54 | precendence: 55 | type: integer 56 | require: 57 | items: 58 | type: string 59 | type: array 60 | required: 61 | - descision 62 | - include 63 | - name 64 | type: object 65 | type: array 66 | allowedIdPs: 67 | items: 68 | type: string 69 | type: array 70 | autoRedirectToIndentiy: 71 | type: boolean 72 | corsHeaders: 73 | properties: 74 | allowAllHeaders: 75 | type: boolean 76 | allowAllMethods: 77 | type: boolean 78 | allowAllOrigins: 79 | type: boolean 80 | allowCredentials: 81 | type: boolean 82 | allowedHeader: 83 | items: 84 | type: string 85 | type: array 86 | allowedMethods: 87 | items: 88 | type: string 89 | type: array 90 | allowedOrigins: 91 | items: 92 | type: string 93 | type: array 94 | maxAge: 95 | type: integer 96 | required: 97 | - allowAllHeaders 98 | - allowAllMethods 99 | - allowAllOrigins 100 | - allowCredentials 101 | - allowedHeader 102 | - allowedMethods 103 | - allowedOrigins 104 | - maxAge 105 | type: object 106 | domain: 107 | type: string 108 | name: 109 | type: string 110 | sessionDuration: 111 | type: string 112 | zone: 113 | type: string 114 | required: 115 | - domain 116 | - name 117 | - zone 118 | type: object 119 | status: 120 | description: AccessApplicationStatus defines the observed state of AccessApplicationS 121 | properties: 122 | applicationID: 123 | type: string 124 | type: object 125 | type: object 126 | version: v1alpha1 127 | versions: 128 | - name: v1alpha1 129 | served: true 130 | storage: true 131 | status: 132 | acceptedNames: 133 | kind: "" 134 | plural: "" 135 | conditions: [] 136 | storedVersions: [] 137 | -------------------------------------------------------------------------------- /config/crds/v1/crds.kubeflare.io_accessapplications.yaml: -------------------------------------------------------------------------------- 1 | 2 | --- 3 | apiVersion: apiextensions.k8s.io/v1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | annotations: 7 | controller-gen.kubebuilder.io/version: v0.4.1 8 | creationTimestamp: null 9 | name: accessapplications.crds.kubeflare.io 10 | spec: 11 | group: crds.kubeflare.io 12 | names: 13 | kind: AccessApplication 14 | listKind: AccessApplicationList 15 | plural: accessapplications 16 | singular: accessapplication 17 | scope: Namespaced 18 | versions: 19 | - name: v1alpha1 20 | schema: 21 | openAPIV3Schema: 22 | description: DNSRecord is the Schema for the accessapplication API 23 | properties: 24 | apiVersion: 25 | description: 'APIVersion defines the versioned schema of this representation 26 | of an object. Servers should convert recognized schemas to the latest 27 | internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' 28 | type: string 29 | kind: 30 | description: 'Kind is a string value representing the REST resource this 31 | object represents. Servers may infer this from the endpoint the client 32 | submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' 33 | type: string 34 | metadata: 35 | type: object 36 | spec: 37 | description: AccessApplicationSpec defines the desired state of AccessApplication 38 | properties: 39 | accessPolicies: 40 | items: 41 | properties: 42 | descision: 43 | type: string 44 | exclude: 45 | items: 46 | type: string 47 | type: array 48 | include: 49 | items: 50 | type: string 51 | type: array 52 | name: 53 | type: string 54 | precendence: 55 | type: integer 56 | require: 57 | items: 58 | type: string 59 | type: array 60 | required: 61 | - descision 62 | - include 63 | - name 64 | type: object 65 | type: array 66 | allowedIdPs: 67 | items: 68 | type: string 69 | type: array 70 | autoRedirectToIndentiy: 71 | type: boolean 72 | corsHeaders: 73 | properties: 74 | allowAllHeaders: 75 | type: boolean 76 | allowAllMethods: 77 | type: boolean 78 | allowAllOrigins: 79 | type: boolean 80 | allowCredentials: 81 | type: boolean 82 | allowedHeader: 83 | items: 84 | type: string 85 | type: array 86 | allowedMethods: 87 | items: 88 | type: string 89 | type: array 90 | allowedOrigins: 91 | items: 92 | type: string 93 | type: array 94 | maxAge: 95 | type: integer 96 | required: 97 | - allowAllHeaders 98 | - allowAllMethods 99 | - allowAllOrigins 100 | - allowCredentials 101 | - allowedHeader 102 | - allowedMethods 103 | - allowedOrigins 104 | - maxAge 105 | type: object 106 | domain: 107 | type: string 108 | name: 109 | type: string 110 | sessionDuration: 111 | type: string 112 | zone: 113 | type: string 114 | required: 115 | - domain 116 | - name 117 | - zone 118 | type: object 119 | status: 120 | description: AccessApplicationStatus defines the observed state of AccessApplicationS 121 | properties: 122 | applicationID: 123 | type: string 124 | type: object 125 | type: object 126 | served: true 127 | storage: true 128 | subresources: 129 | status: {} 130 | status: 131 | acceptedNames: 132 | kind: "" 133 | plural: "" 134 | conditions: [] 135 | storedVersions: [] 136 | -------------------------------------------------------------------------------- /pkg/controller/accessapplication/accessapplication_controller.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Replicated, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package accessapplication 18 | 19 | import ( 20 | "context" 21 | "time" 22 | 23 | "github.com/pkg/errors" 24 | crdsv1alpha1 "github.com/replicatedhq/kubeflare/pkg/apis/crds/v1alpha1" 25 | "github.com/replicatedhq/kubeflare/pkg/controller/shared" 26 | "github.com/replicatedhq/kubeflare/pkg/logger" 27 | "k8s.io/apimachinery/pkg/runtime" 28 | kubeinformers "k8s.io/client-go/informers" 29 | "k8s.io/client-go/kubernetes" 30 | "sigs.k8s.io/controller-runtime/pkg/client" 31 | "sigs.k8s.io/controller-runtime/pkg/controller" 32 | "sigs.k8s.io/controller-runtime/pkg/handler" 33 | "sigs.k8s.io/controller-runtime/pkg/manager" 34 | "sigs.k8s.io/controller-runtime/pkg/reconcile" 35 | "sigs.k8s.io/controller-runtime/pkg/source" 36 | ) 37 | 38 | // Add creates a new AccessApplication Controller and adds it to the Manager with default RBAC. The Manager will set fields on the Controller 39 | // and Start it when the Manager is Started. 40 | func Add(mgr manager.Manager) error { 41 | return add(mgr, newReconciler(mgr)) 42 | } 43 | 44 | // newReconciler returns a new reconcile.Reconciler 45 | func newReconciler(mgr manager.Manager) reconcile.Reconciler { 46 | return &ReconcileAccessApplication{ 47 | Client: mgr.GetClient(), 48 | scheme: mgr.GetScheme(), 49 | } 50 | } 51 | 52 | // add adds a new Controller to mgr with r as the reconcile.Reconciler 53 | func add(mgr manager.Manager, r reconcile.Reconciler) error { 54 | // Create a new controller 55 | c, err := controller.New("accessapplication-controller", mgr, controller.Options{Reconciler: r}) 56 | if err != nil { 57 | return err 58 | } 59 | 60 | // Watch for changes to AccessApplication 61 | err = c.Watch(&source.Kind{ 62 | Type: &crdsv1alpha1.AccessApplication{}, 63 | }, &handler.EnqueueRequestForObject{}) 64 | if err != nil { 65 | return errors.Wrap(err, "failed to start watch on accessapplication") 66 | } 67 | 68 | generatedClient := kubernetes.NewForConfigOrDie(mgr.GetConfig()) 69 | generatedInformers := kubeinformers.NewSharedInformerFactory(generatedClient, time.Minute) 70 | err = mgr.Add(manager.RunnableFunc(func(s <-chan struct{}) error { 71 | generatedInformers.Start(s) 72 | <-s 73 | return nil 74 | })) 75 | if err != nil { 76 | return err 77 | } 78 | 79 | return nil 80 | } 81 | 82 | var _ reconcile.Reconciler = &ReconcileAccessApplication{} 83 | 84 | // ReconcileAccessApplication reconciles a AccessApplication object 85 | type ReconcileAccessApplication struct { 86 | client.Client 87 | scheme *runtime.Scheme 88 | } 89 | 90 | // Reconcile reads that state of the cluster for a ReconcileAccessApplication object and makes changes based on the state read 91 | // and what is in the Zone.Spec 92 | // +kubebuilder:rbac:groups=crds.kubeflare.io,resources=accessapplications,verbs=get;list;watch;create;update;patch;delete 93 | // +kubebuilder:rbac:groups=crds.kubeflare.io,resources=accessapplications/status,verbs=get;update;patch 94 | func (r *ReconcileAccessApplication) Reconcile(request reconcile.Request) (reconcile.Result, error) { 95 | // This reconcile loop will be called for all ReconcileAccessApplication objects 96 | // because of the informer that we have set up 97 | ctx := context.Background() 98 | instance := crdsv1alpha1.AccessApplication{} 99 | err := r.Get(ctx, request.NamespacedName, &instance) 100 | if err != nil { 101 | logger.Error(err) 102 | return reconcile.Result{}, err 103 | } 104 | 105 | zone, err := shared.GetZone(ctx, instance.Namespace, instance.Spec.Zone) 106 | if err != nil { 107 | logger.Error(err) 108 | return reconcile.Result{}, err 109 | } 110 | 111 | cf, err := shared.GetCloudflareAPI(ctx, instance.Namespace, zone.Spec.APIToken) 112 | if err != nil { 113 | logger.Error(err) 114 | return reconcile.Result{}, err 115 | } 116 | 117 | // if the instanace status subresource doesn't contain an application id, update it now 118 | if instance.Status.ApplicationID == "" { 119 | existingApplication, err := findExistingAccessApplication(instance, zone, cf) 120 | if err != nil { 121 | logger.Error(err) 122 | return reconcile.Result{}, nil 123 | } 124 | 125 | if existingApplication != nil { 126 | instance.Status.ApplicationID = existingApplication.ID 127 | err := r.Status().Update(ctx, &instance) 128 | if err != nil { 129 | logger.Error(err) 130 | return reconcile.Result{}, nil 131 | } 132 | } 133 | } 134 | 135 | _, err = ReconcileAccessApplicationInstance(ctx, instance, zone, cf) 136 | if err != nil { 137 | logger.Error(err) 138 | return reconcile.Result{}, err 139 | } 140 | 141 | return reconcile.Result{}, nil 142 | } 143 | -------------------------------------------------------------------------------- /pkg/client/kubeflareclientset/typed/crds/v1alpha1/fake/fake_zone.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Replicated, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | package fake 20 | 21 | import ( 22 | "context" 23 | 24 | v1alpha1 "github.com/replicatedhq/kubeflare/pkg/apis/crds/v1alpha1" 25 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 | labels "k8s.io/apimachinery/pkg/labels" 27 | schema "k8s.io/apimachinery/pkg/runtime/schema" 28 | types "k8s.io/apimachinery/pkg/types" 29 | watch "k8s.io/apimachinery/pkg/watch" 30 | testing "k8s.io/client-go/testing" 31 | ) 32 | 33 | // FakeZones implements ZoneInterface 34 | type FakeZones struct { 35 | Fake *FakeCrdsV1alpha1 36 | ns string 37 | } 38 | 39 | var zonesResource = schema.GroupVersionResource{Group: "crds.kubeflare.io", Version: "v1alpha1", Resource: "zones"} 40 | 41 | var zonesKind = schema.GroupVersionKind{Group: "crds.kubeflare.io", Version: "v1alpha1", Kind: "Zone"} 42 | 43 | // Get takes name of the zone, and returns the corresponding zone object, and an error if there is any. 44 | func (c *FakeZones) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.Zone, err error) { 45 | obj, err := c.Fake. 46 | Invokes(testing.NewGetAction(zonesResource, c.ns, name), &v1alpha1.Zone{}) 47 | 48 | if obj == nil { 49 | return nil, err 50 | } 51 | return obj.(*v1alpha1.Zone), err 52 | } 53 | 54 | // List takes label and field selectors, and returns the list of Zones that match those selectors. 55 | func (c *FakeZones) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.ZoneList, err error) { 56 | obj, err := c.Fake. 57 | Invokes(testing.NewListAction(zonesResource, zonesKind, c.ns, opts), &v1alpha1.ZoneList{}) 58 | 59 | if obj == nil { 60 | return nil, err 61 | } 62 | 63 | label, _, _ := testing.ExtractFromListOptions(opts) 64 | if label == nil { 65 | label = labels.Everything() 66 | } 67 | list := &v1alpha1.ZoneList{ListMeta: obj.(*v1alpha1.ZoneList).ListMeta} 68 | for _, item := range obj.(*v1alpha1.ZoneList).Items { 69 | if label.Matches(labels.Set(item.Labels)) { 70 | list.Items = append(list.Items, item) 71 | } 72 | } 73 | return list, err 74 | } 75 | 76 | // Watch returns a watch.Interface that watches the requested zones. 77 | func (c *FakeZones) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { 78 | return c.Fake. 79 | InvokesWatch(testing.NewWatchAction(zonesResource, c.ns, opts)) 80 | 81 | } 82 | 83 | // Create takes the representation of a zone and creates it. Returns the server's representation of the zone, and an error, if there is any. 84 | func (c *FakeZones) Create(ctx context.Context, zone *v1alpha1.Zone, opts v1.CreateOptions) (result *v1alpha1.Zone, err error) { 85 | obj, err := c.Fake. 86 | Invokes(testing.NewCreateAction(zonesResource, c.ns, zone), &v1alpha1.Zone{}) 87 | 88 | if obj == nil { 89 | return nil, err 90 | } 91 | return obj.(*v1alpha1.Zone), err 92 | } 93 | 94 | // Update takes the representation of a zone and updates it. Returns the server's representation of the zone, and an error, if there is any. 95 | func (c *FakeZones) Update(ctx context.Context, zone *v1alpha1.Zone, opts v1.UpdateOptions) (result *v1alpha1.Zone, err error) { 96 | obj, err := c.Fake. 97 | Invokes(testing.NewUpdateAction(zonesResource, c.ns, zone), &v1alpha1.Zone{}) 98 | 99 | if obj == nil { 100 | return nil, err 101 | } 102 | return obj.(*v1alpha1.Zone), err 103 | } 104 | 105 | // UpdateStatus was generated because the type contains a Status member. 106 | // Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). 107 | func (c *FakeZones) UpdateStatus(ctx context.Context, zone *v1alpha1.Zone, opts v1.UpdateOptions) (*v1alpha1.Zone, error) { 108 | obj, err := c.Fake. 109 | Invokes(testing.NewUpdateSubresourceAction(zonesResource, "status", c.ns, zone), &v1alpha1.Zone{}) 110 | 111 | if obj == nil { 112 | return nil, err 113 | } 114 | return obj.(*v1alpha1.Zone), err 115 | } 116 | 117 | // Delete takes name of the zone and deletes it. Returns an error if one occurs. 118 | func (c *FakeZones) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { 119 | _, err := c.Fake. 120 | Invokes(testing.NewDeleteAction(zonesResource, c.ns, name), &v1alpha1.Zone{}) 121 | 122 | return err 123 | } 124 | 125 | // DeleteCollection deletes a collection of objects. 126 | func (c *FakeZones) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { 127 | action := testing.NewDeleteCollectionAction(zonesResource, c.ns, listOpts) 128 | 129 | _, err := c.Fake.Invokes(action, &v1alpha1.ZoneList{}) 130 | return err 131 | } 132 | 133 | // Patch applies the patch and returns the patched zone. 134 | func (c *FakeZones) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.Zone, err error) { 135 | obj, err := c.Fake. 136 | Invokes(testing.NewPatchSubresourceAction(zonesResource, c.ns, name, pt, data, subresources...), &v1alpha1.Zone{}) 137 | 138 | if obj == nil { 139 | return nil, err 140 | } 141 | return obj.(*v1alpha1.Zone), err 142 | } 143 | -------------------------------------------------------------------------------- /pkg/apis/crds/v1alpha1/zone_types.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Replicated, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1alpha1 18 | 19 | import ( 20 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 21 | ) 22 | 23 | type SecurityHeader struct { 24 | Enabled *bool `json:"enabled,omitempty"` 25 | MaxAge *int `json:"maxAge,omitempty"` 26 | IncludeSubdomains *bool `json:"includeSubdomains,omitempty"` 27 | NoSniff *bool `json:"noSniff,omitempty"` 28 | } 29 | 30 | type MobileRedirect struct { 31 | Status *bool `json:"status,omi2tempty"` 32 | MobileSubdomain *string `json:"mobileSubdomain,omitempty"` 33 | StripURI *bool `json:"stripURI,omitempty"` 34 | } 35 | 36 | type MinifySetting struct { 37 | CSS *bool `json:"css,omitempty"` 38 | HTML *bool `json:"html,omitempty"` 39 | JS *bool `json:"js,omitempty"` 40 | } 41 | 42 | type ZoneSettings struct { 43 | AdvancedDDOS *bool `json:"advancedDDOS,omitempty"` 44 | AlwaysOnline *bool `json:"alwaysOnline,omitempty"` 45 | AlwaysUseHTTPS *bool `json:"alwaysUseHttps,omitempty"` 46 | OpportunisticOnion *bool `json:"opportunisticOnion,omitempty"` 47 | AutomaticHTTPSRewrites *bool `json:"automaticHTTPSRewrites,omitempty"` 48 | BrowserCacheTTL *int `json:"browserCacheTTL,omitempty"` 49 | BrowserCheck *bool `json:"browserCheck,omitempty"` 50 | CacheLevel *string `json:"cacheLevel,omitempty"` 51 | ChallengeTTL *int `json:"challengeTTL,omitempty"` 52 | DevelopmentMode *bool `json:"developmentMode,omitempty"` 53 | EmailObfuscation *bool `json:"emailObfuscation,omitempty"` 54 | HotlinkProtection *bool `json:"hotlinkProtection,omitempty"` 55 | IPGeolocation *bool `json:"ipGeolocation,omitempty"` 56 | IPV6 *bool `json:"ipv6,omitempty"` 57 | Minify *MinifySetting `json:"minify,omitempty"` 58 | MobileRedirect *MobileRedirect `json:"mobileRedirect,omitempty"` 59 | Mirage *bool `json:"mirage,omitempty"` 60 | OriginErrorPagePassThru *bool `json:"originErrorPagePassThru,omitempty"` 61 | OpportunisticEncryption *bool `json:"opportunisticEncryption,omitempty"` 62 | Polish *bool `json:"polish,omitempty"` 63 | WebP *bool `json:"webp,omitempty"` 64 | Brotli *bool `json:"brotli,omitempty"` 65 | PrefetchPreload *bool `json:"prefetchPreload,omitempty"` 66 | PrivacyPass *bool `json:"privacyPass,omitempty"` 67 | ResponseBuffering *bool `json:"responseBuffering,omitempty"` 68 | RocketLoader *bool `json:"rocketLoader,omitempty"` 69 | SecurityHeader *SecurityHeader `json:"securityHeader,omitempty"` 70 | SecurityLevel *string `json:"securityLevel,omitempty"` 71 | ServerSideExclude *bool `json:"serverSideExclude,omitempty"` 72 | SortQueryStringForCache *bool `json:"sortQueryStringForCache,omitempty"` 73 | SSL *bool `json:"ssl,omitempty"` 74 | MinTLSVersion *string `json:"minTLSVersion,omitempty"` 75 | Ciphers []*string `json:"ciphers,omitempty"` 76 | TLS13 *bool `json:"tls13,omitempty"` 77 | TLSClientAuth *bool `json:"tlsClientAuth,omitempty"` 78 | TrueClientIPHeader *bool `json:"trueClientIPHeader,omitempty"` 79 | WAF *bool `json:"waf,omitempty"` 80 | HTTP2 *bool `json:"http2,omitempty"` 81 | HTTP3 *bool `json:"http3,omitempty"` 82 | ZeroRTT *bool `json:"0rtt,omitempty"` 83 | PseudoIPV4 *bool `json:"pseudoIPV4,omitempty"` 84 | Websockets *bool `json:"websockets,omitempty"` 85 | ImageResizing *bool `json:"imageResizing,omitempty"` 86 | HTTP2Prioritization *bool `json:"http2Prioritization,omitempty"` 87 | } 88 | 89 | // ZoneSpec defines the desired state of Zone 90 | type ZoneSpec struct { 91 | APIToken string `json:"apiToken"` 92 | Settings *ZoneSettings `json:"settings,omitempty"` 93 | } 94 | 95 | // ZoneStatus defines the observed state of Zone 96 | type ZoneStatus struct { 97 | } 98 | 99 | // +genclient 100 | // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object 101 | 102 | // Zone is the Schema for the zones API 103 | // +k8s:openapi-gen=true 104 | // +kubebuilder:subresource:status 105 | type Zone struct { 106 | metav1.TypeMeta `json:",inline"` 107 | metav1.ObjectMeta `json:"metadata,omitempty"` 108 | 109 | Spec ZoneSpec `json:"spec,omitempty"` 110 | Status ZoneStatus `json:"status,omitempty"` 111 | } 112 | 113 | // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object 114 | 115 | // ZoneList contains a list of Zone 116 | type ZoneList struct { 117 | metav1.TypeMeta `json:",inline"` 118 | metav1.ListMeta `json:"metadata,omitempty"` 119 | Items []Zone `json:"items"` 120 | } 121 | 122 | func init() { 123 | SchemeBuilder.Register(&Zone{}, &ZoneList{}) 124 | } 125 | -------------------------------------------------------------------------------- /pkg/client/kubeflareclientset/typed/crds/v1alpha1/fake/fake_apitoken.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Replicated, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | package fake 20 | 21 | import ( 22 | "context" 23 | 24 | v1alpha1 "github.com/replicatedhq/kubeflare/pkg/apis/crds/v1alpha1" 25 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 | labels "k8s.io/apimachinery/pkg/labels" 27 | schema "k8s.io/apimachinery/pkg/runtime/schema" 28 | types "k8s.io/apimachinery/pkg/types" 29 | watch "k8s.io/apimachinery/pkg/watch" 30 | testing "k8s.io/client-go/testing" 31 | ) 32 | 33 | // FakeAPITokens implements APITokenInterface 34 | type FakeAPITokens struct { 35 | Fake *FakeCrdsV1alpha1 36 | ns string 37 | } 38 | 39 | var apitokensResource = schema.GroupVersionResource{Group: "crds.kubeflare.io", Version: "v1alpha1", Resource: "apitokens"} 40 | 41 | var apitokensKind = schema.GroupVersionKind{Group: "crds.kubeflare.io", Version: "v1alpha1", Kind: "APIToken"} 42 | 43 | // Get takes name of the aPIToken, and returns the corresponding aPIToken object, and an error if there is any. 44 | func (c *FakeAPITokens) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.APIToken, err error) { 45 | obj, err := c.Fake. 46 | Invokes(testing.NewGetAction(apitokensResource, c.ns, name), &v1alpha1.APIToken{}) 47 | 48 | if obj == nil { 49 | return nil, err 50 | } 51 | return obj.(*v1alpha1.APIToken), err 52 | } 53 | 54 | // List takes label and field selectors, and returns the list of APITokens that match those selectors. 55 | func (c *FakeAPITokens) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.APITokenList, err error) { 56 | obj, err := c.Fake. 57 | Invokes(testing.NewListAction(apitokensResource, apitokensKind, c.ns, opts), &v1alpha1.APITokenList{}) 58 | 59 | if obj == nil { 60 | return nil, err 61 | } 62 | 63 | label, _, _ := testing.ExtractFromListOptions(opts) 64 | if label == nil { 65 | label = labels.Everything() 66 | } 67 | list := &v1alpha1.APITokenList{ListMeta: obj.(*v1alpha1.APITokenList).ListMeta} 68 | for _, item := range obj.(*v1alpha1.APITokenList).Items { 69 | if label.Matches(labels.Set(item.Labels)) { 70 | list.Items = append(list.Items, item) 71 | } 72 | } 73 | return list, err 74 | } 75 | 76 | // Watch returns a watch.Interface that watches the requested aPITokens. 77 | func (c *FakeAPITokens) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { 78 | return c.Fake. 79 | InvokesWatch(testing.NewWatchAction(apitokensResource, c.ns, opts)) 80 | 81 | } 82 | 83 | // Create takes the representation of a aPIToken and creates it. Returns the server's representation of the aPIToken, and an error, if there is any. 84 | func (c *FakeAPITokens) Create(ctx context.Context, aPIToken *v1alpha1.APIToken, opts v1.CreateOptions) (result *v1alpha1.APIToken, err error) { 85 | obj, err := c.Fake. 86 | Invokes(testing.NewCreateAction(apitokensResource, c.ns, aPIToken), &v1alpha1.APIToken{}) 87 | 88 | if obj == nil { 89 | return nil, err 90 | } 91 | return obj.(*v1alpha1.APIToken), err 92 | } 93 | 94 | // Update takes the representation of a aPIToken and updates it. Returns the server's representation of the aPIToken, and an error, if there is any. 95 | func (c *FakeAPITokens) Update(ctx context.Context, aPIToken *v1alpha1.APIToken, opts v1.UpdateOptions) (result *v1alpha1.APIToken, err error) { 96 | obj, err := c.Fake. 97 | Invokes(testing.NewUpdateAction(apitokensResource, c.ns, aPIToken), &v1alpha1.APIToken{}) 98 | 99 | if obj == nil { 100 | return nil, err 101 | } 102 | return obj.(*v1alpha1.APIToken), err 103 | } 104 | 105 | // UpdateStatus was generated because the type contains a Status member. 106 | // Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). 107 | func (c *FakeAPITokens) UpdateStatus(ctx context.Context, aPIToken *v1alpha1.APIToken, opts v1.UpdateOptions) (*v1alpha1.APIToken, error) { 108 | obj, err := c.Fake. 109 | Invokes(testing.NewUpdateSubresourceAction(apitokensResource, "status", c.ns, aPIToken), &v1alpha1.APIToken{}) 110 | 111 | if obj == nil { 112 | return nil, err 113 | } 114 | return obj.(*v1alpha1.APIToken), err 115 | } 116 | 117 | // Delete takes name of the aPIToken and deletes it. Returns an error if one occurs. 118 | func (c *FakeAPITokens) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { 119 | _, err := c.Fake. 120 | Invokes(testing.NewDeleteAction(apitokensResource, c.ns, name), &v1alpha1.APIToken{}) 121 | 122 | return err 123 | } 124 | 125 | // DeleteCollection deletes a collection of objects. 126 | func (c *FakeAPITokens) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { 127 | action := testing.NewDeleteCollectionAction(apitokensResource, c.ns, listOpts) 128 | 129 | _, err := c.Fake.Invokes(action, &v1alpha1.APITokenList{}) 130 | return err 131 | } 132 | 133 | // Patch applies the patch and returns the patched aPIToken. 134 | func (c *FakeAPITokens) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.APIToken, err error) { 135 | obj, err := c.Fake. 136 | Invokes(testing.NewPatchSubresourceAction(apitokensResource, c.ns, name, pt, data, subresources...), &v1alpha1.APIToken{}) 137 | 138 | if obj == nil { 139 | return nil, err 140 | } 141 | return obj.(*v1alpha1.APIToken), err 142 | } 143 | --------------------------------------------------------------------------------