├── .github ├── PULL_REQUEST_TEMPLATE.md ├── semantic.yml └── workflows │ ├── release-docker.yaml │ └── test-release.yaml ├── .gitignore ├── .travis.yml ├── CONTRIBUTING.md ├── Dockerfile ├── Dockerfile.debug ├── LICENSE ├── Makefile ├── README.md ├── apis └── metacontroller │ └── v1alpha1 │ ├── doc.go │ ├── hooks.go │ ├── nameselector.go │ ├── register.go │ ├── roundtrip_test.go │ ├── selector.go │ ├── types.go │ ├── types_generic.go │ └── zz_generated.deepcopy.go ├── build └── build.go ├── client └── generated │ ├── clientset │ └── versioned │ │ ├── clientset.go │ │ ├── doc.go │ │ ├── fake │ │ ├── clientset_generated.go │ │ ├── doc.go │ │ └── register.go │ │ ├── scheme │ │ ├── doc.go │ │ └── register.go │ │ └── typed │ │ └── metacontroller │ │ └── v1alpha1 │ │ ├── compositecontroller.go │ │ ├── controllerrevision.go │ │ ├── decoratorcontroller.go │ │ ├── doc.go │ │ ├── fake │ │ ├── doc.go │ │ ├── fake_compositecontroller.go │ │ ├── fake_controllerrevision.go │ │ ├── fake_decoratorcontroller.go │ │ ├── fake_genericcontroller.go │ │ └── fake_metacontroller_client.go │ │ ├── generated_expansion.go │ │ ├── genericcontroller.go │ │ └── metacontroller_client.go │ ├── informers │ └── externalversions │ │ ├── factory.go │ │ ├── generic.go │ │ ├── internalinterfaces │ │ └── factory_interfaces.go │ │ └── metacontroller │ │ ├── interface.go │ │ └── v1alpha1 │ │ ├── compositecontroller.go │ │ ├── controllerrevision.go │ │ ├── decoratorcontroller.go │ │ ├── genericcontroller.go │ │ └── interface.go │ └── listers │ └── metacontroller │ └── v1alpha1 │ ├── compositecontroller.go │ ├── controllerrevision.go │ ├── decoratorcontroller.go │ ├── expansion_generated.go │ └── genericcontroller.go ├── code-of-conduct.md ├── config ├── config.go ├── config_test.go └── testdata │ ├── test_cctl.yaml │ ├── test_dctl.yaml │ └── test_gctl.yaml ├── controller ├── common │ ├── common.go │ ├── controller.go │ ├── controller_test.go │ ├── finalizer │ │ └── finalizer.go │ ├── invoke_hook.go │ ├── manage_children.go │ ├── manage_children_test.go │ └── selector │ │ ├── evaluator.go │ │ ├── evaluator_test.go │ │ ├── field.go │ │ ├── field_test.go │ │ ├── reference.go │ │ ├── reference_test.go │ │ ├── slice.go │ │ ├── util.go │ │ └── util_test.go ├── composite │ ├── controller.go │ ├── controller_revision.go │ ├── hooks.go │ ├── metacontroller.go │ └── rolling_update.go ├── decorator │ ├── controller.go │ ├── hooks.go │ ├── metacontroller.go │ └── selector.go ├── doc.go └── generic │ ├── controller.go │ ├── controller_test.go │ ├── hooks.go │ ├── inlinehook.go │ ├── metacontroller.go │ ├── metacontroller_test.go │ ├── options.go │ ├── selector.go │ ├── selector_test.go │ ├── testdata │ └── test_gctl.yaml │ └── updatestrategy.go ├── docs ├── .gitignore ├── 404.html ├── _api │ ├── apply.md │ ├── compositecontroller.md │ ├── controllerrevision.md │ ├── decoratorcontroller.md │ └── hook.md ├── _config.yml ├── _contrib │ └── build.md ├── _data │ └── navigation.yml ├── _design │ ├── customize-hook.md │ └── map-controller.md ├── _guide │ ├── best-practices.md │ ├── create.md │ ├── install.md │ └── troubleshooting.md ├── _includes │ └── footer.html ├── _redirects ├── api.md ├── assets │ └── css │ │ └── main.scss ├── concepts.md ├── contrib.md ├── design.md ├── examples.md ├── faq.md ├── features.md ├── go-import.html ├── guide.md ├── intro.md └── pronunciation.md ├── dynamic ├── apply │ ├── apply.go │ └── apply_test.go ├── clientset │ └── clientset.go ├── controllerref │ ├── controller_ref.go │ ├── controller_revision.go │ └── unstructured.go ├── discovery │ └── discovery.go ├── informer │ ├── factory.go │ └── informer.go ├── lister │ └── lister.go └── object │ ├── metadata.go │ ├── metadata_test.go │ └── status.go ├── examples ├── bluegreen │ ├── README.md │ ├── my-bluegreen.yaml │ ├── operator.yaml │ ├── sync.js │ └── test.sh ├── catset │ ├── README.md │ ├── catset-controller.yaml │ ├── my-catset.yaml │ ├── sync.js │ └── test.sh ├── cctl │ └── hello-python │ │ ├── controller.yaml │ │ ├── crd.yaml │ │ ├── readme.md │ │ ├── sync.py │ │ └── webhook.yaml ├── clusteredparent │ ├── README.md │ ├── cluster-parent.yaml │ ├── my-clusterrole.yaml │ ├── sync.py │ └── test.sh ├── crd-roles │ ├── README.md │ ├── crd-role-controller.yaml │ ├── my-crd.yaml │ ├── sync.py │ └── test.sh ├── daemonjob │ ├── README.md │ ├── daemonjob-controller.yaml │ ├── my-daemonjob.yaml │ ├── sync.py │ └── test.sh ├── gctl │ ├── dont-panic │ │ ├── .gitignore │ │ ├── Dockerfile │ │ ├── Makefile │ │ ├── README.md │ │ ├── cmd │ │ │ └── main.go │ │ ├── config │ │ │ └── config.yaml │ │ ├── dontpanic-ns.yaml │ │ ├── dontpanic-operator.yaml │ │ ├── dontpanic-rbac-crd.yaml │ │ ├── dontpanic.yaml │ │ ├── go.mod │ │ ├── go.sum │ │ └── kind-with-registry.sh │ ├── dynamic-config │ │ ├── .gitignore │ │ ├── Dockerfile │ │ ├── Makefile │ │ ├── README.md │ │ ├── cmd │ │ │ └── main.go │ │ ├── config │ │ │ └── controller.yaml │ │ ├── dynamicconfig-ns.yaml │ │ ├── dynamicconfig-operator.yaml │ │ ├── dynamicconfig-rbac-crd.yaml │ │ ├── dynamicconfig.yaml │ │ ├── go.mod │ │ └── go.sum │ ├── hello-world │ │ ├── .gitignore │ │ ├── Dockerfile │ │ ├── Makefile │ │ ├── README.md │ │ ├── cmd │ │ │ └── main.go │ │ ├── config │ │ │ └── config.yaml │ │ ├── go.mod │ │ ├── go.sum │ │ ├── helloworld-ns.yaml │ │ ├── helloworld-operator.yaml │ │ ├── helloworld-rbac-crd.yaml │ │ ├── helloworld.yaml │ │ └── kind-with-registry.sh │ ├── install-uninstall-crd │ │ ├── README.md │ │ ├── hooks │ │ │ ├── finalize-crd.jsonnet │ │ │ └── sync-crd.jsonnet │ │ ├── my-namespace.yaml │ │ ├── operator.yaml │ │ └── test.sh │ ├── set-status-on-cr │ │ ├── .gitignore │ │ ├── Dockerfile │ │ ├── Makefile │ │ ├── README.md │ │ ├── config │ │ │ └── config.yaml │ │ ├── coolnerd.yaml │ │ ├── go.mod │ │ ├── go.sum │ │ ├── main.go │ │ ├── ns_and_crd.yaml │ │ ├── operator.yaml │ │ ├── rbac.yaml │ │ └── test.sh │ └── uninstall-openebs │ │ ├── .gitignore │ │ ├── Dockerfile │ │ ├── Makefile │ │ ├── README.md │ │ ├── config │ │ └── config.yaml │ │ ├── go.mod │ │ ├── go.sum │ │ ├── main.go │ │ ├── openebs_crs.yaml │ │ ├── openebs_ns.yaml │ │ ├── openebs_sa_and_crds.yaml │ │ ├── operator.yaml │ │ ├── operator_ns.yaml │ │ ├── operator_rbac.yaml │ │ └── test.sh ├── go │ ├── .gitignore │ ├── Dockerfile │ ├── Gopkg.lock │ ├── Gopkg.toml │ ├── README.md │ ├── main.go │ ├── my-thing.yaml │ └── thing-controller.yaml ├── indexedjob │ ├── README.md │ ├── indexedjob-controller.yaml │ ├── my-indexedjob.yaml │ ├── sync.py │ └── test.sh ├── jsonnetd │ ├── .gitignore │ ├── Dockerfile │ ├── Gopkg.lock │ ├── Gopkg.toml │ ├── Makefile │ ├── README.md │ ├── extensions.go │ └── main.go ├── kind-with-registry.sh ├── nodejs │ ├── Dockerfile │ ├── Makefile │ ├── README.md │ └── server.js ├── sample-controller │ └── js │ │ ├── README.md │ │ ├── foo.yaml │ │ ├── operator.yaml │ │ ├── sync.js │ │ └── test.sh ├── service-per-pod │ ├── README.md │ ├── hooks │ │ ├── finalize-service-per-pod.jsonnet │ │ ├── sync-pod-name-label.jsonnet │ │ └── sync-service-per-pod.jsonnet │ ├── my-statefulset.yaml │ ├── operator.yaml │ └── test.sh ├── status │ ├── README.md │ ├── my-noop.yaml │ ├── operator.yaml │ ├── sync.js │ └── test.sh ├── test.sh └── vitess │ └── README.md ├── go.mod ├── go.sum ├── hack ├── custom-boilerplate.go.txt ├── get-kube-binaries.sh ├── tools.go ├── update-codegen.sh └── verify-codegen.sh ├── helm └── metac │ ├── Chart.yaml │ ├── README.md │ ├── crds │ ├── metac.openebs.io_compositecontrollers.yaml │ ├── metac.openebs.io_controllerrevisions.yaml │ ├── metac.openebs.io_decoratorcontrollers.yaml │ └── metac.openebs.io_genericcontrollers.yaml │ ├── templates │ ├── NOTES.txt │ ├── _helpers.tpl │ ├── crd-cleanup-job.yaml │ ├── crds.yaml │ ├── role.yaml │ ├── rolebinding.yaml │ ├── serviceaccount.yaml │ └── statefulset.yaml │ └── values.yaml ├── hooks ├── doc.go ├── hooks.go └── webhook │ └── webhook.go ├── kustomization.yaml ├── main.go ├── manifests ├── dev │ ├── args.yaml │ ├── image.yaml │ └── kustomization.yaml ├── metacontroller-namespace.yaml ├── metacontroller-rbac.yaml └── metacontroller.yaml ├── release.config.js ├── server └── server.go ├── skaffold.yaml ├── start └── start.go ├── test └── integration │ ├── config-mode │ ├── set_status_on_cr_test.go │ ├── set_status_on_cr_that_exists_later_test.go │ └── suite_test.go │ ├── crd-mode │ ├── cr_owns_a_pod_cctl_test.go │ ├── cr_owns_a_pod_dctl_test.go │ ├── cr_owns_a_pod_gctl_test.go │ ├── delete_ns_deletes_crd_cr_gctl_test.go │ ├── delete_stuck_ns_gctl_test.go │ ├── explicit_delete_gctl_test.go │ ├── explicit_update_gctl_test.go │ ├── ns_owns_crd_gctl_test.go │ ├── ns_watch_creates_cluster_att_gctl_test.go │ ├── resync_after_seconds_cctl_test.go │ ├── resync_after_seconds_dctl_test.go │ ├── resync_after_seconds_gctl_test.go │ ├── scale_up_then_down_cctl_test.go │ ├── scale_up_then_down_gctl_test.go │ ├── server_side_apply_lbls_anns_finalizers_gctl_test.go │ ├── server_side_apply_with_update_strategy_gctl_test.go │ ├── suite_test.go │ └── sync_cr_status_gctl_inline_hook_test.go │ └── framework │ ├── README.md │ ├── addr │ └── manager.go │ ├── apiserver.go │ ├── apply.go │ ├── assert.go │ ├── assets │ └── bin │ │ └── .gitkeep │ ├── command.go │ ├── control_plane.go │ ├── crd.go │ ├── doc.go │ ├── etcd.go │ ├── fixture.go │ ├── helper.go │ ├── internal │ ├── apiserver.go │ ├── arguments.go │ ├── bin_path_finder.go │ ├── etcd.go │ ├── process.go │ └── tinyca.go │ ├── kubectl.go │ ├── metacontroller.go │ ├── path_check.go │ ├── retry.go │ ├── start.go │ ├── state_check.go │ ├── testing.go │ └── webhook.go └── third_party └── kubernetes ├── LICENSE ├── controller.go ├── controller_ref_manager.go ├── pointer.go ├── unstructured.go ├── yaml.go └── yaml_test.go /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Pull Request template 2 | Please, go through these steps before you submit a PR. 3 | 4 | 1. This repository follows semantic versioning convention, therefore each PR title/commit message must follow convention: `(): [#issue_number] - `. 5 | `type` is defining if release will be triggering after merging submitted changes, details in [CONTRIBUTING.md](../CONTRIBUTING.md). `#issue_number` is optional, when commit resolves any github issue. 6 | Most common types are: 7 | * `feat` - for new features 8 | * `fix` - for bug fixes or improvements 9 | * `chore` - changes not related to code 10 | 11 | 12 | 13 | IMPORTANT: Please review the [CONTRIBUTING.md](../CONTRIBUTING.md) file for detailed contributing guidelines. 14 | 15 | **PLEASE REMOVE THIS TEMPLATE BEFORE SUBMITTING** -------------------------------------------------------------------------------- /.github/semantic.yml: -------------------------------------------------------------------------------- 1 | titleAndCommits: true 2 | allowMergeCommits: true -------------------------------------------------------------------------------- /.github/workflows/release-docker.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Release docker image 3 | on: 4 | push: 5 | tags: 6 | - 'v[0-9]+.[0-9]+.[0-9]+' 7 | jobs: 8 | release-docker: 9 | name: Release docker image 10 | runs-on: ubuntu-18.04 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v2 14 | - name: Deploy docker image 15 | uses: docker/build-push-action@v1 16 | with: 17 | username: ${{ secrets.DOCKER_USERNAME }} 18 | password: ${{ secrets.DOCKER_PASSWORD }} 19 | repository: amitkumardas/metac 20 | registry: quay.io 21 | tag_with_ref: true 22 | add_git_labels: true 23 | -------------------------------------------------------------------------------- /.github/workflows/test-release.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Test and Release (if on master) 3 | on: [push, pull_request] 4 | jobs: 5 | tests: 6 | runs-on: ubuntu-18.04 7 | strategy: 8 | matrix: 9 | test: ['unit-test', 'integration-test'] 10 | name: ${{ matrix.test }} 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v2 14 | - name: setup env 15 | run: | 16 | echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV 17 | echo "$(go env GOPATH)/bin" >> $GITHUB_PATH 18 | - name: Setup go 19 | uses: actions/setup-go@v1 20 | with: 21 | go-version: 1.13.5 22 | - run: make ${{ matrix.test }} 23 | release: 24 | name: Release 25 | runs-on: ubuntu-18.04 26 | needs: ['tests'] 27 | steps: 28 | - name: Checkout 29 | uses: actions/checkout@v1 30 | - name: Setup Node.js 31 | uses: actions/setup-node@v1 32 | with: 33 | node-version: 12 34 | - name: Install dependencies 35 | run: npm install ci 36 | - name: Release 37 | env: 38 | GH_TOKEN: ${{ secrets.PAT }} 39 | run: npx semantic-release 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea/ 2 | /vendor/ 3 | /metac 4 | /hack/bin/ 5 | /test/integration/framework/assets/bin/ 6 | /manifests/crds/ 7 | .*.swp 8 | .history 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | # Force-enable Go modules. Also force go to use the code in vendor/ 4 | # These will both be unnecessary when Go 1.13 lands. 5 | env: 6 | global: 7 | - GO111MODULE=on 8 | # each corresponds to a travis job 9 | # each will be run in parallel 10 | matrix: 11 | - TEST=image 12 | - TEST="unit-test integration-test" 13 | 14 | go: 15 | - 1.12.x 16 | 17 | cache: 18 | directories: 19 | - $GOPATH/pkg/mod 20 | 21 | addons: 22 | apt: 23 | packages: 24 | - wget 25 | 26 | script: 27 | - cd .. 28 | - mkdir -p openebs.io 29 | - mv metac/ openebs.io/ 30 | - cd openebs.io/metac/ 31 | - make $TEST 32 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | We'd love to accept your patches and contributions to this project. There are just a few small guidelines you need to follow. 4 | 5 | ## Contributor License Agreement 6 | 7 | Contributions to this project must be accompanied by a Contributor License Agreement. You (or your employer) retain the copyright to your contribution; this simply gives us permission to use and redistribute your contributions as part of the project. Head over to to see your current agreements on file or to sign a new one. 8 | 9 | You generally only need to submit a CLA once, so if you've already submitted one (even if it was for a different project), you probably don't need to do it again. 10 | 11 | ## Code Reviews 12 | 13 | All submissions, including submissions by project members, require review. We use GitHub pull requests for this purpose. Consult 14 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more information on using pull requests. 15 | 16 | ## Commit messages 17 | 18 | This repository uses [semantic versioning](https://semver.org/), therefore every commit and PR must follow naming convention. We require commit header to be in form `(): [#issue_number] - `, where `type`, `scope` and 19 | `subject` as defined in [angular commit message convention](https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#type). `#issue_number` is optional, if commit resolves any gihub issue. 20 | 21 | Please be aware that new release will be triggered for: 22 | * `feat` - minor release 23 | * `perf/fix` - patch release 24 | 25 | where minor/patch as in semantic versioning definition. 26 | 27 | ## Contributor Guide 28 | 29 | Aside from the above administrative requirements, you can find more 30 | technical details about project internals in the 31 | [contributor guide](https://metacontroller.app/contrib/). 32 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Tester image 2 | FROM golang:1.13.5 as tester 3 | 4 | WORKDIR /go/src/openebs.io/metac/ 5 | 6 | # copy go modules manifests 7 | COPY go.mod go.mod 8 | COPY go.sum go.sum 9 | 10 | # copy build manifests 11 | COPY Makefile Makefile 12 | 13 | # ensure vendoring is up-to-date by running make vendor in your local 14 | # setup 15 | # 16 | # we cache the vendored dependencies before building and copying source 17 | # so that we don't need to re-download when source changes don't invalidate 18 | # our downloaded layer 19 | RUN make vendor 20 | 21 | # copy build manifests 22 | COPY . . 23 | 24 | RUN make unit-test 25 | RUN make integration-test 26 | 27 | # Build metac binary 28 | FROM golang:1.13.5 as builder 29 | 30 | WORKDIR /go/src/openebs.io/metac/ 31 | 32 | # copy go modules manifests 33 | COPY go.mod go.mod 34 | COPY go.sum go.sum 35 | 36 | # copy build manifests 37 | COPY Makefile Makefile 38 | 39 | # ensure vendoring is up-to-date by running make vendor in your local 40 | # setup 41 | # 42 | # we cache the vendored dependencies before building and copying source 43 | # so that we don't need to re-download when source changes don't invalidate 44 | # our downloaded layer 45 | RUN make vendor 46 | 47 | # copy source files 48 | COPY *.go ./ 49 | COPY apis/ apis/ 50 | COPY client/ client/ 51 | COPY config/ config/ 52 | COPY hack/ hack/ 53 | COPY controller/ controller/ 54 | COPY dynamic/ dynamic/ 55 | COPY hooks/ hooks/ 56 | COPY server/ server/ 57 | COPY start/ start/ 58 | COPY third_party/ third_party/ 59 | 60 | # build metacontroller binary 61 | RUN make bins 62 | 63 | # Use a distroless image 64 | # Sort the instructions to be more effective for caching 65 | 66 | FROM gcr.io/distroless/base-debian10:nonroot 67 | 68 | WORKDIR / 69 | USER nonroot:nonroot 70 | CMD ["/usr/bin/metac"] 71 | COPY --from=builder --chown=nonroot /go/src/openebs.io/metac/metac /usr/bin/ 72 | -------------------------------------------------------------------------------- /Dockerfile.debug: -------------------------------------------------------------------------------- 1 | # Tester image 2 | FROM golang:1.13.5 as tester 3 | 4 | WORKDIR /go/src/openebs.io/metac/ 5 | 6 | # copy go modules manifests 7 | COPY go.mod go.mod 8 | COPY go.sum go.sum 9 | 10 | # copy build manifests 11 | COPY Makefile Makefile 12 | 13 | # ensure vendoring is up-to-date by running make vendor in your local 14 | # setup 15 | # 16 | # we cache the vendored dependencies before building and copying source 17 | # so that we don't need to re-download when source changes don't invalidate 18 | # our downloaded layer 19 | RUN make vendor 20 | 21 | # copy build manifests 22 | COPY . . 23 | 24 | RUN make unit-test 25 | RUN make integration-test 26 | 27 | # Build metac binary 28 | FROM golang:1.13.5 as builder 29 | 30 | WORKDIR /go/src/openebs.io/metac/ 31 | 32 | # copy go modules manifests 33 | COPY go.mod go.mod 34 | COPY go.sum go.sum 35 | 36 | # copy build manifests 37 | COPY Makefile Makefile 38 | 39 | # ensure vendoring is up-to-date by running make vendor in your local 40 | # setup 41 | # 42 | # we cache the vendored dependencies before building and copying source 43 | # so that we don't need to re-download when source changes don't invalidate 44 | # our downloaded layer 45 | RUN make vendor 46 | 47 | # copy source files 48 | COPY *.go ./ 49 | COPY apis/ apis/ 50 | COPY client/ client/ 51 | COPY config/ config/ 52 | COPY hack/ hack/ 53 | COPY controller/ controller/ 54 | COPY dynamic/ dynamic/ 55 | COPY hooks/ hooks/ 56 | COPY server/ server/ 57 | COPY start/ start/ 58 | COPY third_party/ third_party/ 59 | 60 | # build metacontroller binary 61 | RUN make bins 62 | 63 | # Use a distroless image 64 | # Sort the instructions to be more effective for caching 65 | 66 | FROM gcr.io/distroless/base-debian10:debug-nonroot 67 | 68 | WORKDIR / 69 | USER nonroot:nonroot 70 | CMD ["/usr/bin/metac"] 71 | COPY --from=builder --chown=nonroot /go/src/openebs.io/metac/metac /usr/bin/ 72 | -------------------------------------------------------------------------------- /apis/metacontroller/v1alpha1/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google 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 | https://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 | // +k8s:deepcopy-gen=package,register 18 | 19 | package v1alpha1 20 | -------------------------------------------------------------------------------- /apis/metacontroller/v1alpha1/hooks.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 The MayaData Authors. 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 | https://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 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 20 | 21 | // Hook refers to the logic that builds the desired 22 | // state of resources 23 | // +kubebuilder:object:generate=false 24 | type Hook struct { 25 | // Webhook invocation to arrive at desired state 26 | Webhook *Webhook `json:"webhook,omitempty"` 27 | 28 | // Inline invocation to arrive at desired state 29 | Inline *Inline `json:"inline,omitempty"` 30 | } 31 | 32 | // Webhook refers to the logic that gets invoked as 33 | // as web hook to arrive at the desired state 34 | type Webhook struct { 35 | URL *string `json:"url,omitempty"` 36 | Timeout *metav1.Duration `json:"timeout,omitempty"` 37 | 38 | Path *string `json:"path,omitempty"` 39 | Service *ServiceReference `json:"service,omitempty"` 40 | } 41 | 42 | // Inline refers to the logic that gets invoked as inline 43 | // function call to arrive at the desired state. 44 | // 45 | // NOTE: 46 | // This works as a single binary that works via meta controller 47 | // invoking this inline hook function. 48 | type Inline struct { 49 | FuncName *string `json:"funcName,omitempty"` 50 | } 51 | -------------------------------------------------------------------------------- /apis/metacontroller/v1alpha1/nameselector.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 The MayaData Authors. 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 | https://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 | // NameSelector is used to select resources based on 20 | // the names set here 21 | type NameSelector []string 22 | 23 | // Contains returns true if the provided search item 24 | // is present in the selector 25 | func (s NameSelector) Contains(search string) bool { 26 | for _, name := range s { 27 | if name == search { 28 | return true 29 | } 30 | } 31 | return false 32 | } 33 | 34 | // ContainsOrTrue returns true if nameselector is empty or 35 | // search item is available in nameselector 36 | func (s NameSelector) ContainsOrTrue(search string) bool { 37 | if len(s) == 0 { 38 | return true 39 | } 40 | return s.Contains(search) 41 | } 42 | -------------------------------------------------------------------------------- /apis/metacontroller/v1alpha1/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 The MayaData Authors. 3 | Copyright 2017 Google Inc. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | https://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | package v1alpha1 19 | 20 | import ( 21 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 22 | "k8s.io/apimachinery/pkg/runtime" 23 | "k8s.io/apimachinery/pkg/runtime/schema" 24 | ) 25 | 26 | var ( 27 | SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) 28 | AddToScheme = SchemeBuilder.AddToScheme 29 | ) 30 | 31 | // GroupName is the group name used in this package. 32 | const GroupName = "metac.openebs.io" 33 | 34 | // SchemeGroupVersion is the group version used to register these objects. 35 | var SchemeGroupVersion = schema.GroupVersion{ 36 | Group: GroupName, 37 | Version: "v1alpha1", 38 | } 39 | 40 | // Resource takes an unqualified resource and returns a Group-qualified GroupResource. 41 | func Resource(resource string) schema.GroupResource { 42 | return SchemeGroupVersion.WithResource(resource).GroupResource() 43 | } 44 | 45 | // addKnownTypes adds the set of types defined in this package to the supplied scheme. 46 | func addKnownTypes(scheme *runtime.Scheme) error { 47 | scheme.AddKnownTypes(SchemeGroupVersion, 48 | &CompositeController{}, 49 | &CompositeControllerList{}, 50 | &DecoratorController{}, 51 | &DecoratorControllerList{}, 52 | &ControllerRevision{}, 53 | &ControllerRevisionList{}, 54 | &GenericController{}, 55 | &GenericControllerList{}, 56 | ) 57 | metav1.AddToGroupVersion(scheme, SchemeGroupVersion) 58 | return nil 59 | } 60 | -------------------------------------------------------------------------------- /apis/metacontroller/v1alpha1/roundtrip_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google 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 | https://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 | "math/rand" 21 | "testing" 22 | 23 | "k8s.io/apimachinery/pkg/api/apitesting/fuzzer" 24 | roundtrip "k8s.io/apimachinery/pkg/api/apitesting/roundtrip" 25 | metafuzzer "k8s.io/apimachinery/pkg/apis/meta/fuzzer" 26 | "k8s.io/apimachinery/pkg/runtime" 27 | "k8s.io/apimachinery/pkg/runtime/serializer" 28 | ) 29 | 30 | // TestRoundTrip tests that the third-party kinds can be marshaled and unmarshaled 31 | // correctly to/from JSON without the loss of information. Moreover, deep copy is 32 | // tested. 33 | func TestRoundTrip(t *testing.T) { 34 | scheme := runtime.NewScheme() 35 | codecs := serializer.NewCodecFactory(scheme) 36 | 37 | AddToScheme(scheme) 38 | 39 | fuzzer := fuzzer.FuzzerFor(metafuzzer.Funcs, rand.NewSource(1), codecs) 40 | 41 | roundtrip.RoundTripSpecificKindWithoutProtobuf(t, SchemeGroupVersion.WithKind("CompositeController"), scheme, codecs, fuzzer, nil) 42 | roundtrip.RoundTripSpecificKindWithoutProtobuf(t, SchemeGroupVersion.WithKind("CompositeControllerList"), scheme, codecs, fuzzer, nil) 43 | roundtrip.RoundTripSpecificKindWithoutProtobuf(t, SchemeGroupVersion.WithKind("DecoratorController"), scheme, codecs, fuzzer, nil) 44 | roundtrip.RoundTripSpecificKindWithoutProtobuf(t, SchemeGroupVersion.WithKind("DecoratorControllerList"), scheme, codecs, fuzzer, nil) 45 | roundtrip.RoundTripSpecificKindWithoutProtobuf(t, SchemeGroupVersion.WithKind("ControllerRevision"), scheme, codecs, fuzzer, nil) 46 | roundtrip.RoundTripSpecificKindWithoutProtobuf(t, SchemeGroupVersion.WithKind("ControllerRevisionList"), scheme, codecs, fuzzer, nil) 47 | } 48 | -------------------------------------------------------------------------------- /build/build.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019 The MayaData Authors. 3 | Copyright (c) 2018 Uber Technologies, Inc. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | package build 19 | 20 | // Hash is a variable that will be populated at build-time of the 21 | // binary via the ldflags parameter. It is used to break cache when a new 22 | // version of this library is used. 23 | var Hash string 24 | 25 | // init is needed for now to go around the github issue listed above. 26 | func init() { 27 | if Hash == "" { 28 | Hash = "master-unreleased" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /client/generated/clientset/versioned/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 The MayaData Authors 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 versioned 21 | -------------------------------------------------------------------------------- /client/generated/clientset/versioned/fake/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 The MayaData Authors 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 | -------------------------------------------------------------------------------- /client/generated/clientset/versioned/fake/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 The MayaData Authors 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 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 23 | runtime "k8s.io/apimachinery/pkg/runtime" 24 | schema "k8s.io/apimachinery/pkg/runtime/schema" 25 | serializer "k8s.io/apimachinery/pkg/runtime/serializer" 26 | utilruntime "k8s.io/apimachinery/pkg/util/runtime" 27 | metacontrollerv1alpha1 "openebs.io/metac/apis/metacontroller/v1alpha1" 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 | metacontrollerv1alpha1.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 | -------------------------------------------------------------------------------- /client/generated/clientset/versioned/scheme/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 The MayaData Authors 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 | -------------------------------------------------------------------------------- /client/generated/clientset/versioned/scheme/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 The MayaData Authors 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 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 23 | runtime "k8s.io/apimachinery/pkg/runtime" 24 | schema "k8s.io/apimachinery/pkg/runtime/schema" 25 | serializer "k8s.io/apimachinery/pkg/runtime/serializer" 26 | utilruntime "k8s.io/apimachinery/pkg/util/runtime" 27 | metacontrollerv1alpha1 "openebs.io/metac/apis/metacontroller/v1alpha1" 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 | metacontrollerv1alpha1.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 | -------------------------------------------------------------------------------- /client/generated/clientset/versioned/typed/metacontroller/v1alpha1/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 The MayaData Authors 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 | -------------------------------------------------------------------------------- /client/generated/clientset/versioned/typed/metacontroller/v1alpha1/fake/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 The MayaData Authors 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 | -------------------------------------------------------------------------------- /client/generated/clientset/versioned/typed/metacontroller/v1alpha1/fake/fake_metacontroller_client.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 The MayaData Authors 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 | rest "k8s.io/client-go/rest" 23 | testing "k8s.io/client-go/testing" 24 | v1alpha1 "openebs.io/metac/client/generated/clientset/versioned/typed/metacontroller/v1alpha1" 25 | ) 26 | 27 | type FakeMetacontrollerV1alpha1 struct { 28 | *testing.Fake 29 | } 30 | 31 | func (c *FakeMetacontrollerV1alpha1) CompositeControllers() v1alpha1.CompositeControllerInterface { 32 | return &FakeCompositeControllers{c} 33 | } 34 | 35 | func (c *FakeMetacontrollerV1alpha1) ControllerRevisions(namespace string) v1alpha1.ControllerRevisionInterface { 36 | return &FakeControllerRevisions{c, namespace} 37 | } 38 | 39 | func (c *FakeMetacontrollerV1alpha1) DecoratorControllers() v1alpha1.DecoratorControllerInterface { 40 | return &FakeDecoratorControllers{c} 41 | } 42 | 43 | func (c *FakeMetacontrollerV1alpha1) GenericControllers(namespace string) v1alpha1.GenericControllerInterface { 44 | return &FakeGenericControllers{c, namespace} 45 | } 46 | 47 | // RESTClient returns a RESTClient that is used to communicate 48 | // with API server by this client implementation. 49 | func (c *FakeMetacontrollerV1alpha1) RESTClient() rest.Interface { 50 | var ret *rest.RESTClient 51 | return ret 52 | } 53 | -------------------------------------------------------------------------------- /client/generated/clientset/versioned/typed/metacontroller/v1alpha1/generated_expansion.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 The MayaData Authors 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 CompositeControllerExpansion interface{} 22 | 23 | type ControllerRevisionExpansion interface{} 24 | 25 | type DecoratorControllerExpansion interface{} 26 | 27 | type GenericControllerExpansion interface{} 28 | -------------------------------------------------------------------------------- /client/generated/informers/externalversions/internalinterfaces/factory_interfaces.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 The MayaData Authors 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 informer-gen. DO NOT EDIT. 18 | 19 | package internalinterfaces 20 | 21 | import ( 22 | time "time" 23 | 24 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 25 | runtime "k8s.io/apimachinery/pkg/runtime" 26 | cache "k8s.io/client-go/tools/cache" 27 | versioned "openebs.io/metac/client/generated/clientset/versioned" 28 | ) 29 | 30 | // NewInformerFunc takes versioned.Interface and time.Duration to return a SharedIndexInformer. 31 | type NewInformerFunc func(versioned.Interface, time.Duration) cache.SharedIndexInformer 32 | 33 | // SharedInformerFactory a small interface to allow for adding an informer without an import cycle 34 | type SharedInformerFactory interface { 35 | Start(stopCh <-chan struct{}) 36 | InformerFor(obj runtime.Object, newFunc NewInformerFunc) cache.SharedIndexInformer 37 | } 38 | 39 | // TweakListOptionsFunc is a function that transforms a v1.ListOptions. 40 | type TweakListOptionsFunc func(*v1.ListOptions) 41 | -------------------------------------------------------------------------------- /client/generated/informers/externalversions/metacontroller/interface.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 The MayaData Authors 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 informer-gen. DO NOT EDIT. 18 | 19 | package metacontroller 20 | 21 | import ( 22 | internalinterfaces "openebs.io/metac/client/generated/informers/externalversions/internalinterfaces" 23 | v1alpha1 "openebs.io/metac/client/generated/informers/externalversions/metacontroller/v1alpha1" 24 | ) 25 | 26 | // Interface provides access to each of this group's versions. 27 | type Interface interface { 28 | // V1alpha1 provides access to shared informers for resources in V1alpha1. 29 | V1alpha1() v1alpha1.Interface 30 | } 31 | 32 | type group struct { 33 | factory internalinterfaces.SharedInformerFactory 34 | namespace string 35 | tweakListOptions internalinterfaces.TweakListOptionsFunc 36 | } 37 | 38 | // New returns a new Interface. 39 | func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { 40 | return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} 41 | } 42 | 43 | // V1alpha1 returns a new v1alpha1.Interface. 44 | func (g *group) V1alpha1() v1alpha1.Interface { 45 | return v1alpha1.New(g.factory, g.namespace, g.tweakListOptions) 46 | } 47 | -------------------------------------------------------------------------------- /client/generated/listers/metacontroller/v1alpha1/expansion_generated.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 The MayaData Authors 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 lister-gen. DO NOT EDIT. 18 | 19 | package v1alpha1 20 | 21 | // CompositeControllerListerExpansion allows custom methods to be added to 22 | // CompositeControllerLister. 23 | type CompositeControllerListerExpansion interface{} 24 | 25 | // ControllerRevisionListerExpansion allows custom methods to be added to 26 | // ControllerRevisionLister. 27 | type ControllerRevisionListerExpansion interface{} 28 | 29 | // ControllerRevisionNamespaceListerExpansion allows custom methods to be added to 30 | // ControllerRevisionNamespaceLister. 31 | type ControllerRevisionNamespaceListerExpansion interface{} 32 | 33 | // DecoratorControllerListerExpansion allows custom methods to be added to 34 | // DecoratorControllerLister. 35 | type DecoratorControllerListerExpansion interface{} 36 | 37 | // GenericControllerListerExpansion allows custom methods to be added to 38 | // GenericControllerLister. 39 | type GenericControllerListerExpansion interface{} 40 | 41 | // GenericControllerNamespaceListerExpansion allows custom methods to be added to 42 | // GenericControllerNamespaceLister. 43 | type GenericControllerNamespaceListerExpansion interface{} 44 | -------------------------------------------------------------------------------- /code-of-conduct.md: -------------------------------------------------------------------------------- 1 | # Kubernetes Community Code of Conduct 2 | 3 | Please refer to our [Kubernetes Community Code of Conduct](https://git.k8s.io/community/code-of-conduct.md) 4 | -------------------------------------------------------------------------------- /config/testdata/test_cctl.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: metac.openebs.io/v1alpha1 3 | kind: CompositeController 4 | metadata: 5 | name: bluegreen-controller 6 | spec: 7 | parentResource: 8 | apiVersion: ctl.enisoc.com/v1 9 | resource: bluegreendeployments 10 | childResources: 11 | - apiVersion: v1 12 | resource: services 13 | updateStrategy: 14 | method: InPlace 15 | - apiVersion: extensions/v1beta1 16 | resource: replicasets 17 | updateStrategy: 18 | method: InPlace 19 | hooks: 20 | sync: 21 | webhook: 22 | url: http://bluegreen-controller.metac/sync 23 | --- -------------------------------------------------------------------------------- /config/testdata/test_dctl.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: metacontroller.k8s.io/v1alpha1 3 | kind: DecoratorController 4 | metadata: 5 | name: cluster-parent 6 | spec: 7 | resources: 8 | - apiVersion: rbac.authorization.k8s.io/v1 9 | resource: clusterroles 10 | annotationSelector: 11 | matchExpressions: 12 | - {key: default-service-account-binding, operator: Exists} 13 | attachments: 14 | - apiVersion: rbac.authorization.k8s.io/v1 15 | resource: rolebindings 16 | hooks: 17 | sync: 18 | webhook: 19 | url: http://cluster-parent-controller.metacontroller/sync 20 | --- -------------------------------------------------------------------------------- /config/testdata/test_gctl.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: metac.openebs.io/v1alpha1 3 | kind: GenericController 4 | metadata: 5 | name: set-status-on-cr 6 | spec: 7 | watch: 8 | apiVersion: examples.metac.io/v1 9 | resource: coolnerds 10 | hooks: 11 | sync: 12 | inline: 13 | funcName: sync/cool-nerd 14 | --- -------------------------------------------------------------------------------- /controller/common/selector/field_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 The MayaData Authors. 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 | https://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 selector 18 | 19 | // TODO 20 | -------------------------------------------------------------------------------- /controller/common/selector/util.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 The MayaData Authors. 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 | https://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 selector 18 | 19 | import ( 20 | "strings" 21 | 22 | "github.com/pkg/errors" 23 | "k8s.io/apimachinery/pkg/util/validation" 24 | ) 25 | 26 | // validateLabelKey validates if the given key is valid 27 | func validateLabelKey(key string) error { 28 | if errs := validation.IsQualifiedName(key); len(errs) != 0 { 29 | return errors.Errorf( 30 | "Invalid label key %q: [%s]", key, strings.Join(errs, "; "), 31 | ) 32 | } 33 | return nil 34 | } 35 | 36 | // validateLabelValue validates if the given value is valid 37 | func validateLabelValue(key, value string) error { 38 | if errs := validation.IsValidLabelValue(value); len(errs) != 0 { 39 | return errors.Errorf( 40 | "Invalid label value %q at key %q: [%s]", value, key, strings.Join(errs, "; "), 41 | ) 42 | } 43 | return nil 44 | } 45 | -------------------------------------------------------------------------------- /controller/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 The MayaData Authors. 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 | https://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 holds the business logic for all the meta 18 | // controllers implemented by metac. The meta controllers that are 19 | // currently supported are: 20 | // 21 | // 1/ Composite Controller 22 | // 2/ Decorator Controller 23 | // 3/ Generic Controller 24 | package controller 25 | -------------------------------------------------------------------------------- /controller/generic/inlinehook.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 The MayaData Authors. 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 | https://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 generic 18 | 19 | import ( 20 | "sync" 21 | 22 | "github.com/pkg/errors" 23 | ) 24 | 25 | // InlineInvokeFn is the signature for all inline hook invocation functions 26 | type InlineInvokeFn func(req *SyncHookRequest, resp *SyncHookResponse) error 27 | 28 | type inlineHookRegistry struct { 29 | sync.Mutex 30 | invokeFuncs map[string]InlineInvokeFn 31 | } 32 | 33 | var inlineHookRegistryInstance = &inlineHookRegistry{ 34 | invokeFuncs: make(map[string]InlineInvokeFn), 35 | } 36 | 37 | // AddToInlineRegistry will add function name and correponding 38 | // function to inline hook registry 39 | func AddToInlineRegistry(funcName string, fn InlineInvokeFn) { 40 | inlineHookRegistryInstance.Lock() 41 | defer inlineHookRegistryInstance.Unlock() 42 | inlineHookRegistryInstance.invokeFuncs[funcName] = fn 43 | } 44 | 45 | // InlineHookInvoker manages invocation of inline hook 46 | type InlineHookInvoker struct { 47 | FuncName string 48 | } 49 | 50 | // NewInlineHookInvoker returns a new instance of inline hook invoker 51 | func NewInlineHookInvoker(funcName string) (*InlineHookInvoker, error) { 52 | if funcName == "" { 53 | return nil, 54 | errors.Errorf("Inline invoker function name can't be empty") 55 | } 56 | return &InlineHookInvoker{FuncName: funcName}, nil 57 | } 58 | 59 | // Invoke this inline hook by passing the given request 60 | // and fill up the given response with the hook's response 61 | func (i *InlineHookInvoker) Invoke(req *SyncHookRequest, resp *SyncHookResponse) error { 62 | fn := inlineHookRegistryInstance.invokeFuncs[i.FuncName] 63 | if fn == nil { 64 | return errors.Errorf( 65 | "Inline hook function not found for %s", i.FuncName, 66 | ) 67 | } 68 | return fn(req, resp) 69 | } 70 | -------------------------------------------------------------------------------- /controller/generic/testdata/test_gctl.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: metac.openebs.io/v1alpha1 3 | kind: GenericController 4 | metadata: 5 | name: set-status-on-cr 6 | spec: 7 | watch: 8 | apiVersion: examples.metac.io/v1 9 | resource: coolnerds 10 | hooks: 11 | sync: 12 | inline: 13 | funcName: sync/cool-nerd 14 | --- -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | _site 2 | .sass-cache 3 | .jekyll-metadata 4 | -------------------------------------------------------------------------------- /docs/404.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | --- 4 | 5 | 18 | 19 |
20 |

404

21 | 22 |

Page not found :(

23 |

The requested page could not be found.

24 |
25 | -------------------------------------------------------------------------------- /docs/_api/hook.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Hook 3 | classes: wide 4 | --- 5 | This page describes how hook targets are defined in various APIs. 6 | 7 | Each hook that you define as part of using one of the hook-based APIs 8 | has the following fields: 9 | 10 | | Field | Description | 11 | | ----- | ----------- | 12 | | [webhook](#webhook) | Specify how to invoke this hook over HTTP(S). | 13 | 14 | ## Example 15 | 16 | ```yaml 17 | webhook: 18 | url: http://my-controller-svc/sync 19 | ``` 20 | 21 | ## Webhook 22 | 23 | Each Webhook has the following fields: 24 | 25 | | Field | Description | 26 | | ----- | ----------- | 27 | | url | A full URL for the webhook (e.g. `http://my-controller-svc/hook`). If present, this overrides any values provided for `path` and `service`. | 28 | | timeout | A duration (in the format of Go's time.Duration) indicating the time that Metacontroller should wait for a response. If the webhook takes longer than this time, the webhook call is aborted and retried later. Defaults to 10s. | 29 | | path | A path to be appended to the accompanying `service` to reach this hook (e.g. `/hook`). Ignored if full `url` is specified. | 30 | | [service](#service-reference) | A reference to a Kubernetes Service through which this hook can be reached. | 31 | 32 | ### Service Reference 33 | 34 | Within a `webhook`, the `service` field has the following subfields: 35 | 36 | | Field | Description | 37 | | ----- | ----------- | 38 | | name | The `metadata.name` of the target Service. | 39 | | namespace | The `metadata.namespace` of the target Service. | 40 | | port | The port number to connect to on the target Service. Defaults to `80`. | 41 | | protocol | The protocol to use for the target Service. Defaults to `http`. | -------------------------------------------------------------------------------- /docs/_data/navigation.yml: -------------------------------------------------------------------------------- 1 | main: 2 | - title: GitHub 3 | url: https://github.com/GoogleCloudPlatform/metacontroller 4 | - title: Slack 5 | url: https://kubernetes.slack.com/messages/metacontroller/ 6 | - title: Forum 7 | url: https://groups.google.com/forum/#!forum/metacontroller 8 | - title: Announcements 9 | url: https://groups.google.com/forum/#!forum/metacontroller-announce 10 | 11 | docs: 12 | - title: Getting Started 13 | url: / 14 | children: 15 | - title: Introduction 16 | url: / 17 | - title: Examples 18 | url: /examples/ 19 | - title: Concepts 20 | url: /concepts/ 21 | - title: Features 22 | url: /features/ 23 | - title: FAQ 24 | url: /faq/ 25 | - title: User Guide 26 | url: /guide/ 27 | children: 28 | - title: Install Metacontroller 29 | url: /guide/install/ 30 | - title: Create a Controller 31 | url: /guide/create/ 32 | - title: Best Practices 33 | url: /guide/best-practices/ 34 | - title: Troubleshooting 35 | url: /guide/troubleshooting/ 36 | - title: API Reference 37 | url: /api/ 38 | children: 39 | - title: CompositeController 40 | url: /api/compositecontroller/ 41 | - title: DecoratorController 42 | url: /api/decoratorcontroller/ 43 | - title: Contributing 44 | url: /contrib/ 45 | -------------------------------------------------------------------------------- /docs/_guide/install.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Installation 3 | classes: wide 4 | toc: false 5 | --- 6 | This page describes how to install Metacontroller, either to develop your own 7 | controllers or just to run third-party controllers that depend on it. 8 | 9 | ## Prerequisites 10 | 11 | * Kubernetes v1.9+ 12 | * You should have `kubectl` available and configured to talk to the desired cluster. 13 | 14 | ### Grant yourself cluster-admin (GKE only) 15 | 16 | Due to a [known issue](https://cloud.google.com/container-engine/docs/role-based-access-control#defining_permissions_in_a_role) 17 | in GKE, you'll need to first grant yourself `cluster-admin` privileges before 18 | you can install the necessary RBAC manifests. 19 | 20 | ```sh 21 | kubectl create clusterrolebinding -cluster-admin-binding --clusterrole=cluster-admin --user=@ 22 | ``` 23 | 24 | Replace `` and `` above based on the account you use to authenticate to GKE. 25 | 26 | ## Install Metacontroller 27 | 28 | ```sh 29 | # Create metacontroller namespace. 30 | kubectl create namespace metacontroller 31 | # Create metacontroller service account and role/binding. 32 | kubectl apply -f {{ site.repo_raw }}/manifests/metacontroller-rbac.yaml 33 | # Create CRDs for Metacontroller APIs, and the Metacontroller StatefulSet. 34 | kubectl apply -f {{ site.repo_raw }}/manifests/metacontroller.yaml 35 | ``` 36 | 37 | If you prefer to build and host your own images, please see the 38 | [build instructions](/contrib/build/) in the contributor guide. 39 | 40 | ## Configuration 41 | 42 | The Metacontroller server has a few settings that can be configured 43 | with command-line flags (by editing the Metacontroller StatefulSet 44 | in `manifests/metacontroller.yaml`): 45 | 46 | | Flag | Description | 47 | | ---- | ----------- | 48 | | `-v` | Set the logging verbosity level (e.g. `-v=4`). Level 4 logs Metacontroller's interaction with the API server. Levels 5 and up additionally log details of Metacontroller's invocation of lambda hooks. See the [troubleshooting guide](/guide/troubleshooting/) for more. | 49 | | `--discovery-interval` | How often to refresh discovery cache to pick up newly-installed resources (e.g. `--discovery-interval=10s`). | 50 | | `--cache-flush-interval` | How often to flush local caches and relist objects from the API server (e.g. `--cache-flush-interval=30m`). | 51 | -------------------------------------------------------------------------------- /docs/_includes/footer.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/_redirects: -------------------------------------------------------------------------------- 1 | https://metacontroller.netlify.com/* https://metacontroller.app/:splat 301! 2 | /* go-get=1 /go-import.html 200! 3 | -------------------------------------------------------------------------------- /docs/api.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: API Reference 3 | layout: collection 4 | collection: api 5 | sort_by: title 6 | permalink: /api/ 7 | classes: wide 8 | --- 9 | This section contains detailed reference information for the APIs offered by Metacontroller. 10 | 11 | See the [user guide](/guide/) for introductions and step-by-step walkthroughs. -------------------------------------------------------------------------------- /docs/assets/css/main.scss: -------------------------------------------------------------------------------- 1 | --- 2 | # Only the main Sass file needs front matter (the dashes are enough) 3 | --- 4 | 5 | @charset "utf-8"; 6 | 7 | @import "minimal-mistakes/skins/{{ site.minimal_mistakes_skin | default: 'default' }}"; // skin 8 | @import "minimal-mistakes"; // main partials 9 | 10 | .page__content p, 11 | .page__content li, 12 | .archive p { 13 | font-size: .8em; 14 | } 15 | 16 | .masthead__menu-item { 17 | font-size: 20px; 18 | } 19 | 20 | .nav-tag { 21 | font-size: 80%; 22 | } 23 | 24 | a { 25 | text-decoration: none; 26 | } 27 | 28 | .page__content code { 29 | white-space: pre; 30 | } -------------------------------------------------------------------------------- /docs/contrib.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Contributor Guide 3 | layout: collection 4 | collection: contrib 5 | sort_by: title 6 | permalink: /contrib/ 7 | classes: wide 8 | --- 9 | This section contains information for people who want to hack on or 10 | contribute to Metacontroller. 11 | 12 | See the [User Guide](/guide/) if you just want to use Metacontroller. 13 | 14 | ## GitHub 15 | 16 | * [Issues]({{ site.repo_url }}/issues) 17 | * [Project Boards]({{ site.repo_url }}/projects) 18 | * [Roadmap]({{ site.repo_url }}/issues/9) 19 | -------------------------------------------------------------------------------- /docs/design.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Design Docs 3 | layout: collection 4 | collection: design 5 | sort_by: title 6 | permalink: /design/ 7 | --- 8 | -------------------------------------------------------------------------------- /docs/go-import.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /docs/guide.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: User Guide 3 | layout: collection 4 | collection: guide 5 | sort_by: title 6 | permalink: /guide/ 7 | classes: wide 8 | --- 9 | This section contains general tips and step-by-step tutorials for using Metacontroller. 10 | 11 | See the [API Reference](/api/) for details about all the available options. 12 | -------------------------------------------------------------------------------- /docs/pronunciation.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: How to pronounce Metacontroller 3 | permalink: /pronunciation/ 4 | --- 5 | *Metacontroller* is pronounced as *me-ta-con-trol-ler*. -------------------------------------------------------------------------------- /dynamic/controllerref/controller_ref.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 Google 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 | https://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 controllerref 18 | 19 | import ( 20 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 21 | "k8s.io/apimachinery/pkg/types" 22 | ) 23 | 24 | func addOwnerReference( 25 | in []metav1.OwnerReference, 26 | add metav1.OwnerReference, 27 | ) []metav1.OwnerReference { 28 | out := make([]metav1.OwnerReference, 0, len(in)+1) 29 | found := false 30 | for _, ref := range in { 31 | if ref.UID == add.UID { 32 | // We already own this. Update other fields as needed. 33 | out = append(out, add) 34 | found = true 35 | continue 36 | } 37 | out = append(out, ref) 38 | } 39 | if !found { 40 | // Add ourselves to the list. 41 | // Note that server-side validation is responsible for 42 | // ensuring only one ControllerRef. 43 | out = append(out, add) 44 | } 45 | return out 46 | } 47 | 48 | func removeOwnerReference(in []metav1.OwnerReference, uid types.UID) []metav1.OwnerReference { 49 | out := make([]metav1.OwnerReference, 0, len(in)) 50 | for _, ref := range in { 51 | if ref.UID != uid { 52 | out = append(out, ref) 53 | } 54 | } 55 | return out 56 | } 57 | -------------------------------------------------------------------------------- /examples/bluegreen/README.md: -------------------------------------------------------------------------------- 1 | ## BlueGreenDeployment 2 | 3 | This is an example CompositeController that implements a custom rollout strategy based on a technique called Blue-Green Deployment. 4 | 5 | The controller ramps up a completely separate ReplicaSet in the background for any change to the Pod template. It then waits for the new ReplicaSet to be fully Ready and Available (all Pods satisfy minReadySeconds), and then switches a Service to point to the new ReplicaSet. Finally, it scales down the old ReplicaSet. 6 | 7 | ### Prerequisites 8 | 9 | * Install Metac from the manifests folder 10 | 11 | ```sh 12 | # verify presence of metac CRDs 13 | kubectl get crd 14 | 15 | # verify presence of metac controller 16 | kubectl get sts -n metac 17 | ``` 18 | 19 | ### Deploy the BlueGreen controller 20 | 21 | ```sh 22 | kubectl create configmap bluegreen-controller -n metac --from-file=sync.js 23 | 24 | kubectl apply -f operator.yaml 25 | 26 | # verify presence of BlueGreen CRD 27 | kubectl get crd 28 | 29 | # verify presence of BlueGreen controller deployment & service 30 | kubectl get deploy -n metac 31 | kubectl get svc -n metac 32 | ``` 33 | 34 | ### Create a BlueGreenDeployment 35 | 36 | ```sh 37 | kubectl apply -f my-bluegreen.yaml 38 | ``` 39 | -------------------------------------------------------------------------------- /examples/bluegreen/my-bluegreen.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: ctl.enisoc.com/v1 2 | kind: BlueGreenDeployment 3 | metadata: 4 | name: nginx 5 | labels: 6 | app: nginx 7 | spec: 8 | replicas: 3 9 | minReadySeconds: 5 10 | selector: 11 | matchLabels: 12 | app: nginx 13 | component: frontend 14 | template: 15 | metadata: 16 | labels: 17 | app: nginx 18 | component: frontend 19 | spec: 20 | containers: 21 | - name: nginx 22 | image: nginx:1.7.9 23 | ports: 24 | - containerPort: 80 25 | service: 26 | metadata: 27 | name: nginx-frontend 28 | labels: 29 | app: nginx 30 | component: frontend 31 | spec: 32 | selector: 33 | app: nginx 34 | component: frontend 35 | ports: 36 | - port: 80 37 | -------------------------------------------------------------------------------- /examples/bluegreen/operator.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1beta1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | name: bluegreendeployments.ctl.enisoc.com 5 | spec: 6 | group: ctl.enisoc.com 7 | version: v1 8 | scope: Namespaced 9 | names: 10 | plural: bluegreendeployments 11 | singular: bluegreendeployment 12 | kind: BlueGreenDeployment 13 | shortNames: 14 | - bgd 15 | subresources: 16 | status: {} 17 | --- 18 | apiVersion: metac.openebs.io/v1alpha1 19 | kind: CompositeController 20 | metadata: 21 | name: bluegreen-controller 22 | spec: 23 | parentResource: 24 | apiVersion: ctl.enisoc.com/v1 25 | resource: bluegreendeployments 26 | childResources: 27 | - apiVersion: v1 28 | resource: services 29 | updateStrategy: 30 | method: InPlace 31 | - apiVersion: extensions/v1beta1 32 | resource: replicasets 33 | updateStrategy: 34 | method: InPlace 35 | hooks: 36 | sync: 37 | webhook: 38 | url: http://bluegreen-controller.metac/sync 39 | --- 40 | apiVersion: apps/v1 41 | kind: Deployment 42 | metadata: 43 | name: bluegreen-controller 44 | namespace: metac 45 | spec: 46 | replicas: 1 47 | selector: 48 | matchLabels: 49 | app: bluegreen-controller 50 | template: 51 | metadata: 52 | labels: 53 | app: bluegreen-controller 54 | spec: 55 | containers: 56 | - name: controller 57 | image: metacontroller/nodejs-server:0.1 58 | imagePullPolicy: Always 59 | volumeMounts: 60 | - name: hooks 61 | mountPath: /node/hooks 62 | volumes: 63 | - name: hooks 64 | configMap: 65 | name: bluegreen-controller 66 | --- 67 | apiVersion: v1 68 | kind: Service 69 | metadata: 70 | name: bluegreen-controller 71 | namespace: metac 72 | spec: 73 | selector: 74 | app: bluegreen-controller 75 | ports: 76 | - port: 80 77 | -------------------------------------------------------------------------------- /examples/catset/README.md: -------------------------------------------------------------------------------- 1 | ## CatSet 2 | 3 | This is a reimplementation of StatefulSet (now including rolling updates) as a CompositeController. 4 | 5 | CatSet also demonstrates using a finalizer with a lambda hook to support 6 | graceful, ordered teardown when the parent object is deleted. 7 | Unlike StatefulSet, which previously exhibited this behavior only because of a 8 | client-side kubectl feature, CatSet ordered teardown happens on the server side, 9 | so it works when the CatSet is deleted through any means (not just kubectl). 10 | 11 | For this example, you need a cluster with a default storage class and a dynamic provisioner. 12 | 13 | ### Prerequisites 14 | 15 | * Install [Metacontroller](https://github.com/GoogleCloudPlatform/metacontroller) 16 | 17 | ### Deploy the controller 18 | 19 | ```sh 20 | kubectl create configmap catset-controller -n metacontroller --from-file=sync.js 21 | kubectl apply -f catset-controller.yaml 22 | ``` 23 | 24 | ### Create a CatSet 25 | 26 | ```sh 27 | kubectl apply -f my-catset.yaml 28 | ``` 29 | -------------------------------------------------------------------------------- /examples/catset/catset-controller.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1beta1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | name: catsets.ctl.enisoc.com 5 | spec: 6 | group: ctl.enisoc.com 7 | version: v1 8 | scope: Namespaced 9 | names: 10 | plural: catsets 11 | singular: catset 12 | kind: CatSet 13 | shortNames: 14 | - cs 15 | subresources: 16 | status: {} 17 | --- 18 | apiVersion: metacontroller.k8s.io/v1alpha1 19 | kind: CompositeController 20 | metadata: 21 | name: catset-controller 22 | spec: 23 | parentResource: 24 | apiVersion: ctl.enisoc.com/v1 25 | resource: catsets 26 | revisionHistory: 27 | fieldPaths: 28 | - spec.template 29 | childResources: 30 | - apiVersion: v1 31 | resource: pods 32 | updateStrategy: 33 | method: RollingRecreate 34 | statusChecks: 35 | conditions: 36 | - type: Ready 37 | status: "True" 38 | - apiVersion: v1 39 | resource: persistentvolumeclaims 40 | hooks: 41 | sync: 42 | webhook: 43 | url: http://catset-controller.metacontroller/sync 44 | finalize: 45 | webhook: 46 | url: http://catset-controller.metacontroller/sync 47 | --- 48 | apiVersion: apps/v1beta1 49 | kind: Deployment 50 | metadata: 51 | name: catset-controller 52 | namespace: metacontroller 53 | spec: 54 | replicas: 1 55 | selector: 56 | matchLabels: 57 | app: catset-controller 58 | template: 59 | metadata: 60 | labels: 61 | app: catset-controller 62 | spec: 63 | containers: 64 | - name: controller 65 | image: metacontroller/nodejs-server:0.1 66 | imagePullPolicy: Always 67 | volumeMounts: 68 | - name: hooks 69 | mountPath: /node/hooks 70 | volumes: 71 | - name: hooks 72 | configMap: 73 | name: catset-controller 74 | --- 75 | apiVersion: v1 76 | kind: Service 77 | metadata: 78 | name: catset-controller 79 | namespace: metacontroller 80 | spec: 81 | selector: 82 | app: catset-controller 83 | ports: 84 | - port: 80 85 | -------------------------------------------------------------------------------- /examples/catset/my-catset.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: nginx-backend 5 | spec: 6 | ports: 7 | - port: 80 8 | name: web 9 | clusterIP: None 10 | selector: 11 | app: nginx 12 | --- 13 | apiVersion: ctl.enisoc.com/v1 14 | kind: CatSet 15 | metadata: 16 | name: nginx-backend 17 | spec: 18 | serviceName: nginx-backend 19 | replicas: 3 20 | selector: 21 | matchLabels: 22 | app: nginx 23 | template: 24 | metadata: 25 | labels: 26 | app: nginx 27 | component: backend 28 | spec: 29 | terminationGracePeriodSeconds: 1 30 | containers: 31 | - name: nginx 32 | image: gcr.io/google_containers/nginx-slim:0.8 33 | ports: 34 | - containerPort: 80 35 | name: web 36 | volumeMounts: 37 | - name: www 38 | mountPath: /usr/share/nginx/html 39 | volumeClaimTemplates: 40 | - metadata: 41 | name: www 42 | labels: 43 | app: nginx 44 | component: backend 45 | spec: 46 | accessModes: [ "ReadWriteOnce" ] 47 | resources: 48 | requests: 49 | storage: 1Gi 50 | -------------------------------------------------------------------------------- /examples/catset/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cleanup() { 4 | set +e 5 | echo "Clean up..." 6 | kubectl patch $cs nginx-backend --type=merge -p '{"metadata":{"finalizers":[]}}' 7 | kubectl delete -f my-catset.yaml 8 | kubectl delete po,pvc -l app=nginx,component=backend 9 | kubectl delete -f catset-controller.yaml 10 | kubectl delete configmap catset-controller -n metacontroller 11 | } 12 | trap cleanup EXIT 13 | 14 | set -ex 15 | 16 | cs="catsets" 17 | finalizer="metacontroller.app/catset-test" 18 | 19 | echo "Install controller..." 20 | kubectl create configmap catset-controller -n metacontroller --from-file=sync.js 21 | kubectl apply -f catset-controller.yaml 22 | 23 | echo "Wait until CRD is available..." 24 | until kubectl get $cs; do sleep 1; done 25 | 26 | echo "Create an object..." 27 | kubectl apply -f my-catset.yaml 28 | 29 | echo "Wait for 3 Pods to be Ready..." 30 | until [[ "$(kubectl get $cs nginx-backend -o 'jsonpath={.status.readyReplicas}')" -eq 3 ]]; do sleep 1; done 31 | 32 | echo "Scale up to 4 replicas..." 33 | kubectl patch $cs nginx-backend --type=merge -p '{"spec":{"replicas":4}}' 34 | 35 | echo "Wait for 4 Pods to be Ready..." 36 | until [[ "$(kubectl get $cs nginx-backend -o 'jsonpath={.status.readyReplicas}')" -eq 4 ]]; do sleep 1; done 37 | 38 | echo "Scale down to 2 replicas..." 39 | kubectl patch $cs nginx-backend --type=merge -p '{"spec":{"replicas":2}}' 40 | 41 | echo "Wait for 2 Pods to be Ready..." 42 | until [[ "$(kubectl get $cs nginx-backend -o 'jsonpath={.status.readyReplicas}')" -eq 2 ]]; do sleep 1; done 43 | 44 | echo "Append our own finalizer so we can read the final state..." 45 | kubectl patch $cs nginx-backend --type=json -p '[{"op":"add","path":"/metadata/finalizers/-","value":"'${finalizer}'"}]' 46 | 47 | echo "Delete CatSet..." 48 | kubectl delete $cs nginx-backend --wait=false 49 | 50 | echo "Expect CatSet's finalizer to scale the CatSet to 0 replicas..." 51 | until [[ "$(kubectl get $cs nginx-backend -o 'jsonpath={.status.replicas}')" -eq 0 ]]; do sleep 1; done 52 | 53 | echo "Wait for our finalizer to be the only one left, then remove it..." 54 | until [[ "$(kubectl get $cs nginx-backend -o 'jsonpath={.metadata.finalizers}')" == "[${finalizer}]" ]]; do sleep 1; done 55 | kubectl patch $cs nginx-backend --type=merge -p '{"metadata":{"finalizers":[]}}' 56 | -------------------------------------------------------------------------------- /examples/cctl/hello-python/controller.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: metac.openebs.io/v1alpha1 2 | kind: CompositeController 3 | metadata: 4 | name: hello-controller 5 | spec: 6 | generateSelector: true 7 | parentResource: 8 | apiVersion: example.com/v1 9 | resource: helloworlds 10 | childResources: 11 | - apiVersion: v1 12 | resource: pods 13 | updateStrategy: 14 | method: Recreate 15 | hooks: 16 | sync: 17 | webhook: 18 | url: http://hello-controller.hello/sync 19 | -------------------------------------------------------------------------------- /examples/cctl/hello-python/crd.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1beta1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | name: helloworlds.example.com 5 | spec: 6 | group: example.com 7 | version: v1 8 | names: 9 | kind: HelloWorld 10 | plural: helloworlds 11 | singular: helloworld 12 | scope: Namespaced 13 | subresources: 14 | status: {} 15 | -------------------------------------------------------------------------------- /examples/cctl/hello-python/sync.py: -------------------------------------------------------------------------------- 1 | from http.server import BaseHTTPRequestHandler, HTTPServer 2 | import json 3 | 4 | 5 | class Controller(BaseHTTPRequestHandler): 6 | def sync(self, parent, children): 7 | # Compute status based on observed state. 8 | desired_status = { 9 | "pods": len(children["Pod.v1"]) 10 | } 11 | 12 | # Generate the desired child object(s). 13 | who = parent.get("spec", {}).get("who", "World") 14 | desired_pods = [ 15 | { 16 | "apiVersion": "v1", 17 | "kind": "Pod", 18 | "metadata": { 19 | "name": parent["metadata"]["name"] 20 | }, 21 | "spec": { 22 | "restartPolicy": "OnFailure", 23 | "containers": [ 24 | { 25 | "name": "hello", 26 | "image": "busybox", 27 | "command": ["echo", "Hello, %s!" % who] 28 | } 29 | ] 30 | } 31 | } 32 | ] 33 | 34 | return {"status": desired_status, "children": desired_pods} 35 | 36 | def do_POST(self): 37 | # Serve the sync() function as a JSON webhook. 38 | observed = json.loads(self.rfile.read( 39 | int(self.headers.get("content-length")))) 40 | desired = self.sync(observed["parent"], observed["children"]) 41 | 42 | self.send_response(200) 43 | self.send_header("Content-type", "application/json") 44 | self.end_headers() 45 | self.wfile.write(json.dumps(desired).encode()) 46 | 47 | 48 | HTTPServer(("", 80), Controller).serve_forever() 49 | -------------------------------------------------------------------------------- /examples/cctl/hello-python/webhook.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: hello-controller 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: hello-controller 10 | template: 11 | metadata: 12 | labels: 13 | app: hello-controller 14 | spec: 15 | containers: 16 | - name: controller 17 | image: python:3.7 18 | command: ["python", "/hooks/sync.py"] 19 | volumeMounts: 20 | - name: hooks 21 | mountPath: /hooks 22 | volumes: 23 | - name: hooks 24 | configMap: 25 | name: hello-controller 26 | --- 27 | apiVersion: v1 28 | kind: Service 29 | metadata: 30 | name: hello-controller 31 | spec: 32 | selector: 33 | app: hello-controller 34 | ports: 35 | - port: 80 36 | -------------------------------------------------------------------------------- /examples/clusteredparent/README.md: -------------------------------------------------------------------------------- 1 | ## ClusterRole service account binding 2 | 3 | This is an example DecoratorController that creates a namespaced resources from a 4 | cluster scoped parent resource. 5 | 6 | This controller will bind any ClusterRole with the "default-service-account-binding" 7 | annotation to the default service account in the default namespace. 8 | 9 | ### Prerequisites 10 | 11 | * Install [Metacontroller](https://github.com/GoogleCloudPlatform/metacontroller) 12 | 13 | ### Deploy the controller 14 | 15 | ```sh 16 | kubectl create configmap cluster-parent-controller -n metacontroller --from-file=sync.py 17 | kubectl apply -f cluster-parent.yaml 18 | ``` 19 | 20 | ### Create a ClusterRole 21 | 22 | ```sh 23 | kubectl apply -f my-clusterole.yaml 24 | ``` 25 | 26 | A RoleBinding should be created for the ClusterRole: 27 | 28 | ```console 29 | $ kubectl get rolebinding -n default my-clusterrole -o wide 30 | NAME AGE ROLE USERS GROUPS SERVICEACCOUNTS 31 | my-clusterrole 40s ClusterRole/my-clusterrole default/default 32 | ``` 33 | -------------------------------------------------------------------------------- /examples/clusteredparent/cluster-parent.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: metacontroller.k8s.io/v1alpha1 2 | kind: DecoratorController 3 | metadata: 4 | name: cluster-parent 5 | spec: 6 | resources: 7 | - apiVersion: rbac.authorization.k8s.io/v1 8 | resource: clusterroles 9 | annotationSelector: 10 | matchExpressions: 11 | - {key: default-service-account-binding, operator: Exists} 12 | attachments: 13 | - apiVersion: rbac.authorization.k8s.io/v1 14 | resource: rolebindings 15 | hooks: 16 | sync: 17 | webhook: 18 | url: http://cluster-parent-controller.metacontroller/sync 19 | --- 20 | apiVersion: apps/v1beta1 21 | kind: Deployment 22 | metadata: 23 | name: clusterparent-controller 24 | namespace: metacontroller 25 | spec: 26 | replicas: 1 27 | selector: 28 | matchLabels: 29 | app: cluster-parent-controller 30 | template: 31 | metadata: 32 | labels: 33 | app: cluster-parent-controller 34 | spec: 35 | containers: 36 | - name: controller 37 | image: python:2.7 38 | command: ["python", "/hooks/sync.py"] 39 | volumeMounts: 40 | - name: hooks 41 | mountPath: /hooks 42 | volumes: 43 | - name: hooks 44 | configMap: 45 | name: cluster-parent-controller 46 | --- 47 | apiVersion: v1 48 | kind: Service 49 | metadata: 50 | name: cluster-parent-controller 51 | namespace: metacontroller 52 | spec: 53 | selector: 54 | app: cluster-parent-controller 55 | ports: 56 | - port: 80 57 | -------------------------------------------------------------------------------- /examples/clusteredparent/my-clusterrole.yaml: -------------------------------------------------------------------------------- 1 | kind: ClusterRole 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | metadata: 4 | name: my-clusterrole 5 | annotations: 6 | default-service-account-binding: "default" 7 | rules: 8 | - apiGroups: [""] 9 | resources: ["nodes"] 10 | verbs: ["get", "watch", "list"] 11 | --- 12 | -------------------------------------------------------------------------------- /examples/clusteredparent/sync.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright 2017 Google Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # https://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer 18 | import json 19 | 20 | def new_rolebinding(name): 21 | rolebinding = {} 22 | rolebinding['apiVersion'] = 'rbac.authorization.k8s.io/v1' 23 | rolebinding['kind'] = 'RoleBinding' 24 | rolebinding['metadata'] = {} 25 | rolebinding['metadata']['name'] = name 26 | rolebinding['metadata']['namespace'] = "default" 27 | rolebinding['subjects'] = [{'kind': 'ServiceAccount', 'name': 'default', 'namespace': 'default'}] 28 | rolebinding['roleRef'] = {'kind': 'ClusterRole', 'name': name, 'apiGroup': 'rbac.authorization.k8s.io'} 29 | return rolebinding 30 | 31 | class Controller(BaseHTTPRequestHandler): 32 | def sync(self, clusterrole, children): 33 | return {'attachments': [new_rolebinding(clusterrole['metadata']['name'])] } 34 | 35 | 36 | def do_POST(self): 37 | observed = json.loads(self.rfile.read(int(self.headers.getheader('content-length')))) 38 | desired = self.sync(observed['object'], observed['attachments']) 39 | 40 | self.send_response(200) 41 | self.send_header('Content-type', 'application/json') 42 | self.end_headers() 43 | self.wfile.write(json.dumps(desired)) 44 | 45 | HTTPServer(('', 80), Controller).serve_forever() 46 | -------------------------------------------------------------------------------- /examples/clusteredparent/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cleanup() { 4 | set +e 5 | echo "Clean up..." 6 | kubectl delete -f my-clusterrole.yaml 7 | kubectl delete -f cluster-parent.yaml 8 | kubectl delete configmap cluster-parent-controller -n metacontroller 9 | } 10 | trap cleanup EXIT 11 | 12 | set -ex 13 | 14 | echo "Install controller..." 15 | kubectl create configmap cluster-parent-controller -n metacontroller --from-file=sync.py 16 | kubectl apply -f cluster-parent.yaml 17 | 18 | echo "Create a ClusterRole..." 19 | kubectl apply -f my-clusterrole.yaml 20 | 21 | echo "Wait for Namespaced child..." 22 | until [[ "$(kubectl get rolebinding -n default my-clusterrole -o 'jsonpath={.metadata.name}')" == "my-clusterrole" ]]; do sleep 1; done 23 | 24 | echo "Delete Namespaced child..." 25 | kubectl delete rolebinding -n default my-clusterrole --wait=true 26 | 27 | # Test that the controller with cluster-scoped parent notices the namespaced child got deleted. 28 | echo "Wait for Namespaced child to be recreated..." 29 | until [[ "$(kubectl get rolebinding -n default my-clusterrole -o 'jsonpath={.metadata.name}')" == "my-clusterrole" ]]; do sleep 1; done 30 | 31 | # Test to make sure cascading deletion of cross namespaces resources works. 32 | echo "Deleting ClusterRole..." 33 | kubectl delete -f my-clusterrole.yaml 34 | 35 | echo "Wait for Namespaced child cleanup..." 36 | until [[ "$(kubectl get clusterrole.rbac.authorization.k8s.io -n default my-clusterrole 2>&1 )" == *NotFound* ]]; do sleep 1; done 37 | -------------------------------------------------------------------------------- /examples/crd-roles/README.md: -------------------------------------------------------------------------------- 1 | ## CRD Roles Controller 2 | 3 | This is an example DecoratorController that manages Cluster scoped resources. 4 | Both the parent and child resouces are Cluster scoped. 5 | 6 | ### Prerequisites 7 | 8 | * Install [Metacontroller](https://github.com/GoogleCloudPlatform/metacontroller) 9 | 10 | ### Deploy the controller 11 | 12 | ```sh 13 | kubectl create configmap crd-role-contoller -n metacontroller --from-file=sync.py 14 | kubectl apply -f crd-role-controller.yaml 15 | ``` 16 | 17 | ### Create a CRD 18 | 19 | ```sh 20 | kubectl apply -f my-crd.yaml 21 | ``` 22 | 23 | A ClusterRole should be created configured with read access to the CRD. 24 | 25 | ```console 26 | $ kubectl get clusterrole my-crd-reader 27 | NAME AGE 28 | my-crd-reader 3m 29 | ``` 30 | -------------------------------------------------------------------------------- /examples/crd-roles/crd-role-controller.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: metacontroller.k8s.io/v1alpha1 3 | kind: DecoratorController 4 | metadata: 5 | name: crd-role-controller 6 | spec: 7 | resources: 8 | - apiVersion: apiextensions.k8s.io/v1beta1 9 | resource: customresourcedefinitions 10 | annotationSelector: 11 | matchExpressions: 12 | - {key: enable-default-roles, operator: Exists} 13 | attachments: 14 | - apiVersion: rbac.authorization.k8s.io/v1 15 | resource: clusterroles 16 | hooks: 17 | sync: 18 | webhook: 19 | url: http://crd-role-controller.metacontroller/sync-crd-role 20 | timeout: 10s 21 | --- 22 | apiVersion: apps/v1beta1 23 | kind: Deployment 24 | metadata: 25 | name: crd-role-controller 26 | namespace: metacontroller 27 | spec: 28 | replicas: 1 29 | selector: 30 | matchLabels: 31 | app: crd-role-controller 32 | template: 33 | metadata: 34 | labels: 35 | app: crd-role-controller 36 | spec: 37 | containers: 38 | - name: controller 39 | image: python:2.7 40 | command: ["python", "/hooks/sync.py"] 41 | volumeMounts: 42 | - name: hooks 43 | mountPath: /hooks 44 | volumes: 45 | - name: hooks 46 | configMap: 47 | name: crd-role-controller 48 | --- 49 | apiVersion: v1 50 | kind: Service 51 | metadata: 52 | name: crd-role-controller 53 | namespace: metacontroller 54 | spec: 55 | selector: 56 | app: crd-role-controller 57 | ports: 58 | - port: 80 59 | -------------------------------------------------------------------------------- /examples/crd-roles/my-crd.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1beta1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | name: my-tests.ctl.rlg.io 5 | annotations: 6 | enable-default-roles: "yes" 7 | spec: 8 | group: ctl.rlg.io 9 | version: v1 10 | scope: Cluster 11 | names: 12 | plural: my-tests 13 | singular: my-test 14 | kind: MyTest 15 | -------------------------------------------------------------------------------- /examples/crd-roles/sync.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright 2017 Google Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # https://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer 18 | import json 19 | 20 | def new_cluster_role(crd): 21 | cr = {} 22 | cr['apiVersion'] = 'rbac.authorization.k8s.io/v1' 23 | cr['kind'] = "ClusterRole" 24 | cr['metadata'] = {} 25 | cr['metadata']['name'] = crd['metadata']['name'] + "-reader" 26 | apiGroup = crd['spec']['group'] 27 | resource = crd['spec']['names']['plural'] 28 | cr['rules'] = [] 29 | # cr['rules'] = [{'apiGroups': [apiGroup], 'resouces':[resource], 'verbs': ["*"]}] 30 | return cr 31 | 32 | class Controller(BaseHTTPRequestHandler): 33 | 34 | def do_POST(self): 35 | observed = json.loads(self.rfile.read(int(self.headers.getheader('content-length')))) 36 | desired = {'attachments': [new_cluster_role(observed['object'])]} 37 | 38 | self.send_response(200) 39 | self.send_header('Content-type', 'application/json') 40 | self.end_headers() 41 | self.wfile.write(json.dumps(desired)) 42 | 43 | HTTPServer(('', 80), Controller).serve_forever() 44 | -------------------------------------------------------------------------------- /examples/crd-roles/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cleanup() { 4 | set +e 5 | echo "Clean up..." 6 | kubectl delete -f my-crd.yaml 7 | kubectl delete -f crd-role-controller.yaml 8 | kubectl delete configmap crd-role-controller -n metacontroller 9 | } 10 | trap cleanup EXIT 11 | 12 | set -ex 13 | 14 | echo "Install controller..." 15 | kubectl create configmap crd-role-controller -n metacontroller --from-file=sync.py 16 | kubectl apply -f crd-role-controller.yaml 17 | 18 | echo "Create a CRD..." 19 | kubectl apply -f my-crd.yaml 20 | 21 | echo "Wait for ClusterRole..." 22 | until [[ "$(kubectl get clusterrole my-tests.ctl.rlg.io-reader -o 'jsonpath={.metadata.name}')" == "my-tests.ctl.rlg.io-reader" ]]; do sleep 1; done 23 | -------------------------------------------------------------------------------- /examples/daemonjob/README.md: -------------------------------------------------------------------------------- 1 | ## DaemonJob 2 | 3 | This is an example CompositeController that's similar to Job, 4 | except that a pod will be scheduled to each node, similar to DaemonSet. 5 | 6 | The implementation was inspired by this [blog post](http://blog.itaysk.com/2017/12/26/the-single-use-daemonset-pattern-and-prepulling-images-in-kubernetes). 7 | This particular pattern has some caveats, like how multiple containers run in 8 | series instead of concurrently, but it suffices as an example of using 9 | CompositeController to wrap up any such pattern you could think of. 10 | 11 | ### Prerequisites 12 | 13 | * Install [Metacontroller](https://github.com/GoogleCloudPlatform/metacontroller) 14 | 15 | ### Deploy the controller 16 | 17 | ```sh 18 | kubectl create configmap daemonjob-controller -n metacontroller --from-file=sync.py 19 | kubectl apply -f daemonjob-controller.yaml 20 | ``` 21 | 22 | ### Create a DaemonJob 23 | 24 | In a separate terminal watch for DaemonJobs, DaemonSets and Pods 25 | 26 | ```sh 27 | watch -n1 kubectl get ds,dj,po 28 | ``` 29 | 30 | Create the DaemonJob 31 | 32 | ```sh 33 | kubectl apply -f my-daemonjob.yaml 34 | ``` 35 | 36 | In the terminal where you have the `watch` command running you will see a 37 | DaemonSet being created as soon as the DaemonJob is deployed and then a pod will 38 | start on each node of your cluster. 39 | These pods will stay in the init stage for about 30s (sleep command in the 40 | container) and will be terminated as soon as all of them have reached the 41 | `Running` state. 42 | The DeamonSet generated by the DaemonJob will also be cleaned up after all the 43 | Pods are done. 44 | 45 | ### Clean up 46 | 47 | ```sh 48 | kubectl delete -f daemonjob-controller.yaml 49 | kubectl delete configmap -n metacontroller daemonjob-controller 50 | ``` 51 | -------------------------------------------------------------------------------- /examples/daemonjob/daemonjob-controller.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1beta1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | name: daemonjobs.ctl.example.com 5 | spec: 6 | group: ctl.example.com 7 | version: v1 8 | scope: Namespaced 9 | names: 10 | plural: daemonjobs 11 | singular: daemonjob 12 | kind: DaemonJob 13 | shortNames: ["dj"] 14 | subresources: 15 | status: {} 16 | --- 17 | apiVersion: metacontroller.k8s.io/v1alpha1 18 | kind: CompositeController 19 | metadata: 20 | name: daemonjob-controller 21 | spec: 22 | generateSelector: true 23 | parentResource: 24 | apiVersion: ctl.example.com/v1 25 | resource: daemonjobs 26 | childResources: 27 | - apiVersion: apps/v1 28 | resource: daemonsets 29 | hooks: 30 | sync: 31 | webhook: 32 | url: http://daemonjob-controller.metacontroller/sync 33 | --- 34 | apiVersion: apps/v1 35 | kind: Deployment 36 | metadata: 37 | name: daemonjob-controller 38 | namespace: metacontroller 39 | spec: 40 | replicas: 1 41 | selector: 42 | matchLabels: 43 | app: daemonjob-controller 44 | template: 45 | metadata: 46 | labels: 47 | app: daemonjob-controller 48 | spec: 49 | containers: 50 | - name: controller 51 | image: python:2.7 52 | command: ["python", "/hooks/sync.py"] 53 | volumeMounts: 54 | - name: hooks 55 | mountPath: /hooks 56 | volumes: 57 | - name: hooks 58 | configMap: 59 | name: daemonjob-controller 60 | --- 61 | apiVersion: v1 62 | kind: Service 63 | metadata: 64 | name: daemonjob-controller 65 | namespace: metacontroller 66 | spec: 67 | selector: 68 | app: daemonjob-controller 69 | ports: 70 | - port: 80 71 | -------------------------------------------------------------------------------- /examples/daemonjob/my-daemonjob.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: ctl.example.com/v1 2 | kind: DaemonJob 3 | metadata: 4 | name: hello-world 5 | spec: 6 | template: 7 | metadata: 8 | labels: 9 | app: hello-world 10 | spec: 11 | containers: 12 | - name: hello-world 13 | image: busybox 14 | command: ["sh", "-c", "echo 'Hello world' && sleep 30"] 15 | resources: 16 | requests: 17 | cpu: 10m 18 | terminationGracePeriodSeconds: 10 19 | -------------------------------------------------------------------------------- /examples/daemonjob/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cleanup() { 4 | set +e 5 | echo "Clean up..." 6 | kubectl delete -f my-daemonjob.yaml 7 | kubectl delete daemonset hello-world-dj 8 | kubectl delete po -l app=hello-world 9 | kubectl delete -f daemonjob-controller.yaml 10 | kubectl delete configmap daemonjob-controller -n metacontroller 11 | } 12 | trap cleanup EXIT 13 | 14 | set -ex 15 | 16 | dj="daemonjobs" 17 | 18 | echo "Install controller..." 19 | kubectl create configmap daemonjob-controller -n metacontroller --from-file=sync.py 20 | kubectl apply -f daemonjob-controller.yaml 21 | 22 | echo "Wait until CRD is available..." 23 | until kubectl get $dj; do sleep 1; done 24 | 25 | echo "Create an object..." 26 | kubectl apply -f my-daemonjob.yaml 27 | 28 | echo "Wait for successful completion..." 29 | until [[ "$(kubectl get $dj hello-world -o 'jsonpath={.status.conditions[0].status}')" == "True" ]]; do sleep 1; done 30 | 31 | echo "Check that DaemonSet gets cleaned up after finishing..." 32 | until [[ "$(kubectl get daemonset hello-world-dj 2>&1)" =~ NotFound ]]; do sleep 1; done 33 | -------------------------------------------------------------------------------- /examples/gctl/dont-panic/.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | -------------------------------------------------------------------------------- /examples/gctl/dont-panic/Dockerfile: -------------------------------------------------------------------------------- 1 | # -------------------------- 2 | # Build d-operators binary 3 | # -------------------------- 4 | FROM golang:1.13.5 as builder 5 | 6 | WORKDIR /example.io/dontpanic/ 7 | 8 | # copy go modules manifests 9 | COPY go.mod go.mod 10 | COPY go.sum go.sum 11 | 12 | COPY Makefile Makefile 13 | 14 | COPY vendor/ vendor/ 15 | 16 | # ensure vendoring is up-to-date by running make vendor 17 | # in your local setup 18 | # 19 | # we cache the vendored dependencies before building and 20 | # copying source so that we don't need to re-download when 21 | # source changes don't invalidate our downloaded layer 22 | RUN go mod download 23 | RUN go mod tidy 24 | RUN go mod vendor 25 | 26 | # copy source file(s) 27 | COPY cmd/ cmd/ 28 | 29 | # build the binary 30 | RUN make 31 | 32 | # --------------------------- 33 | # Use distroless as minimal base image to package the final binary 34 | # Refer https://github.com/GoogleContainerTools/distroless 35 | # --------------------------- 36 | FROM gcr.io/distroless/static:nonroot 37 | 38 | WORKDIR / 39 | 40 | COPY --from=builder /example.io/dontpanic/dontpanic . 41 | COPY config/config.yaml /etc/config/metac/ 42 | 43 | USER nonroot:nonroot 44 | 45 | CMD ["/dontpanic"] 46 | -------------------------------------------------------------------------------- /examples/gctl/dont-panic/Makefile: -------------------------------------------------------------------------------- 1 | ALL_SRC = $(shell find . -name "*.go" | grep -v -e "vendor") 2 | 3 | IMG_NAME ?= dontpanic 4 | PACKAGE_VERSION ?= latest 5 | 6 | all: bins 7 | 8 | bins: vendor $(IMG_NAME) 9 | 10 | $(IMG_NAME): $(ALL_SRC) 11 | @echo "+ Generating $(IMG_NAME) binary" 12 | @CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on \ 13 | go build -o $@ ./cmd/main.go 14 | 15 | $(ALL_SRC): ; 16 | 17 | # go mod download modules to local cache 18 | # make vendored copy of dependencies 19 | # install other go binaries for code generation 20 | .PHONY: vendor 21 | vendor: go.mod go.sum 22 | @GO111MODULE=on go mod download 23 | @GO111MODULE=on go mod tidy 24 | @GO111MODULE=on go mod vendor 25 | 26 | .PHONY: image 27 | image: 28 | docker build -t $(IMG_NAME):$(PACKAGE_VERSION) . -------------------------------------------------------------------------------- /examples/gctl/dont-panic/config/config.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: metac.openebs.io/v1alpha1 2 | kind: GenericController 3 | metadata: 4 | name: dontpanic-controller 5 | spec: 6 | watch: 7 | apiVersion: example.com/v1 8 | resource: dontpanics 9 | attachments: 10 | - apiVersion: notsure.com/v1 11 | resource: iamerrors 12 | advancedSelector: 13 | selectorTerms: 14 | - matchReferenceExpressions: 15 | # select IAmError if its annotation 16 | # 17 | # matches DontPanic _(i.e. watch)_ UID 18 | - key: metadata.annotations.dontpanic/uid 19 | operator: EqualsWatchUID 20 | hooks: 21 | sync: 22 | inline: 23 | funcName: sync/dontpanic 24 | finalize: 25 | inline: 26 | funcName: finalize/dontpanic -------------------------------------------------------------------------------- /examples/gctl/dont-panic/dontpanic-ns.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: dontpanic -------------------------------------------------------------------------------- /examples/gctl/dont-panic/dontpanic-operator.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: StatefulSet 4 | metadata: 5 | labels: 6 | app.mayadata.io/name: dontpanic 7 | name: dontpanic 8 | namespace: dontpanic 9 | spec: 10 | replicas: 1 11 | serviceName: "" 12 | selector: 13 | matchLabels: 14 | app.mayadata.io/name: dontpanic 15 | template: 16 | metadata: 17 | labels: 18 | app.mayadata.io/name: dontpanic 19 | spec: 20 | serviceAccountName: dontpanic 21 | containers: 22 | - name: dontpanic 23 | image: localhost:5000/dontpanic:latest # local registry 24 | command: ["/dontpanic"] 25 | args: 26 | - --logtostderr 27 | - --run-as-local 28 | - --workers-count=1 # number of workers per controller 29 | - --discovery-interval=40s 30 | - --cache-flush-interval=240s # re-sync interval 31 | - -v=7 # this will give us startup errors if any 32 | --- -------------------------------------------------------------------------------- /examples/gctl/dont-panic/dontpanic-rbac-crd.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: dontpanic 5 | namespace: dontpanic 6 | --- 7 | kind: ClusterRole 8 | apiVersion: rbac.authorization.k8s.io/v1beta1 9 | metadata: 10 | name: dontpanic 11 | rules: 12 | - apiGroups: ["example.com"] 13 | resources: ["dontpanics"] 14 | verbs: ["get","watch", "update", "list"] 15 | --- 16 | kind: ClusterRoleBinding 17 | apiVersion: rbac.authorization.k8s.io/v1beta1 18 | metadata: 19 | name: dontpanic 20 | subjects: 21 | - kind: ServiceAccount 22 | name: dontpanic 23 | namespace: dontpanic 24 | roleRef: 25 | kind: ClusterRole 26 | name: dontpanic 27 | apiGroup: rbac.authorization.k8s.io 28 | --- 29 | apiVersion: apiextensions.k8s.io/v1beta1 30 | kind: CustomResourceDefinition 31 | metadata: 32 | name: dontpanics.example.com 33 | spec: 34 | group: example.com 35 | version: v1 36 | names: 37 | kind: DontPanic 38 | plural: dontpanics 39 | singular: dontpanic 40 | scope: Namespaced -------------------------------------------------------------------------------- /examples/gctl/dont-panic/dontpanic.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: example.com/v1 2 | kind: DontPanic 3 | metadata: 4 | name: do-not-panic 5 | namespace: dontpanic -------------------------------------------------------------------------------- /examples/gctl/dont-panic/go.mod: -------------------------------------------------------------------------------- 1 | module example.io/dontpanic 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/coreos/etcd v3.3.15+incompatible // indirect 7 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b 8 | github.com/pkg/errors v0.9.1 9 | golang.org/x/sys v0.0.0-20200113162924-86b910548bc1 // indirect 10 | k8s.io/apimachinery v0.17.3 11 | k8s.io/client-go v0.17.3 // indirect 12 | openebs.io/metac v0.2.1 13 | ) 14 | 15 | replace ( 16 | k8s.io/apimachinery => k8s.io/apimachinery v0.17.3 17 | k8s.io/client-go => k8s.io/client-go v0.17.3 18 | openebs.io/metac => github.com/AmitKumarDas/metac v0.2.1 19 | ) 20 | -------------------------------------------------------------------------------- /examples/gctl/dont-panic/kind-with-registry.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -o errexit 3 | 4 | # desired cluster name; default is "kind" 5 | KIND_CLUSTER_NAME="${KIND_CLUSTER_NAME:-kind}" 6 | 7 | # create registry container unless it already exists 8 | reg_name='kind-registry' 9 | reg_port='5000' 10 | running="$(docker inspect -f '{{.State.Running}}' "${reg_name}" 2>/dev/null || true)" 11 | if [ "${running}" != 'true' ]; then 12 | docker run \ 13 | -d --restart=always -p "${reg_port}:5000" --name "${reg_name}" \ 14 | registry:2 15 | fi 16 | reg_ip="$(docker inspect -f '{{.NetworkSettings.IPAddress}}' "${reg_name}")" 17 | 18 | # create a cluster with the local registry enabled in containerd 19 | cat < k8s.io/apimachinery v0.17.3 17 | k8s.io/client-go => k8s.io/client-go v0.17.3 18 | openebs.io/metac => github.com/AmitKumarDas/metac v0.2.1 19 | ) 20 | -------------------------------------------------------------------------------- /examples/gctl/hello-world/.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | -------------------------------------------------------------------------------- /examples/gctl/hello-world/Dockerfile: -------------------------------------------------------------------------------- 1 | # -------------------------- 2 | # Build d-operators binary 3 | # -------------------------- 4 | FROM golang:1.13.5 as builder 5 | 6 | WORKDIR /example.io/hello-world/ 7 | 8 | # copy go modules manifests 9 | COPY go.mod go.mod 10 | COPY go.sum go.sum 11 | 12 | COPY Makefile Makefile 13 | 14 | COPY vendor/ vendor/ 15 | 16 | # ensure vendoring is up-to-date by running make vendor 17 | # in your local setup 18 | # 19 | # we cache the vendored dependencies before building and 20 | # copying source so that we don't need to re-download when 21 | # source changes don't invalidate our downloaded layer 22 | RUN go mod download 23 | RUN go mod tidy 24 | RUN go mod vendor 25 | 26 | # copy source file(s) 27 | COPY cmd/ cmd/ 28 | 29 | # build the binary 30 | RUN make 31 | 32 | # --------------------------- 33 | # Use distroless as minimal base image to package the final binary 34 | # Refer https://github.com/GoogleContainerTools/distroless 35 | # --------------------------- 36 | FROM gcr.io/distroless/static:nonroot 37 | 38 | WORKDIR / 39 | 40 | COPY --from=builder /example.io/hello-world/hello-world . 41 | COPY config/config.yaml /etc/config/metac/ 42 | 43 | USER nonroot:nonroot 44 | 45 | CMD ["/hello-world"] 46 | -------------------------------------------------------------------------------- /examples/gctl/hello-world/Makefile: -------------------------------------------------------------------------------- 1 | ALL_SRC = $(shell find . -name "*.go" | grep -v -e "vendor") 2 | 3 | IMG_NAME ?= hello-world 4 | PACKAGE_VERSION ?= latest 5 | 6 | all: bins 7 | 8 | bins: vendor $(IMG_NAME) 9 | 10 | $(IMG_NAME): $(ALL_SRC) 11 | @echo "+ Generating $(IMG_NAME) binary" 12 | @CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on \ 13 | go build -o $@ ./cmd/main.go 14 | 15 | $(ALL_SRC): ; 16 | 17 | # go mod download modules to local cache 18 | # make vendored copy of dependencies 19 | # install other go binaries for code generation 20 | .PHONY: vendor 21 | vendor: go.mod go.sum 22 | @GO111MODULE=on go mod download 23 | @GO111MODULE=on go mod tidy 24 | @GO111MODULE=on go mod vendor 25 | 26 | .PHONY: image 27 | image: 28 | docker build -t $(IMG_NAME):$(PACKAGE_VERSION) . -------------------------------------------------------------------------------- /examples/gctl/hello-world/config/config.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: metac.openebs.io/v1alpha1 2 | kind: GenericController 3 | metadata: 4 | name: hello-world-controller 5 | spec: 6 | watch: 7 | apiVersion: example.com/v1 8 | resource: helloworlds 9 | attachments: 10 | - apiVersion: v1 11 | resource: pods 12 | updateStrategy: 13 | method: Recreate 14 | advancedSelector: 15 | selectorTerms: 16 | - matchReferenceExpressions: 17 | # select Pod if its annotation 18 | # 19 | # matches HelloWorld _(i.e. watch)_ UID 20 | - key: metadata.annotations.helloworld/uid 21 | operator: EqualsWatchUID 22 | hooks: 23 | sync: 24 | inline: 25 | funcName: sync/helloworld 26 | finalize: 27 | inline: 28 | funcName: finalize/helloworld -------------------------------------------------------------------------------- /examples/gctl/hello-world/go.mod: -------------------------------------------------------------------------------- 1 | module example.io/hello-world 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/coreos/etcd v3.3.15+incompatible // indirect 7 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b 8 | github.com/pkg/errors v0.9.1 9 | golang.org/x/sys v0.0.0-20200113162924-86b910548bc1 // indirect 10 | k8s.io/apimachinery v0.17.3 11 | k8s.io/client-go v0.17.3 // indirect 12 | openebs.io/metac v0.2.1 13 | ) 14 | 15 | replace ( 16 | k8s.io/apimachinery => k8s.io/apimachinery v0.17.3 17 | k8s.io/client-go => k8s.io/client-go v0.17.3 18 | openebs.io/metac => github.com/AmitKumarDas/metac v0.2.1 19 | ) 20 | -------------------------------------------------------------------------------- /examples/gctl/hello-world/helloworld-ns.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: hello-world -------------------------------------------------------------------------------- /examples/gctl/hello-world/helloworld-operator.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: StatefulSet 4 | metadata: 5 | labels: 6 | app.mayadata.io/name: hello-world 7 | name: hello-world 8 | namespace: hello-world 9 | spec: 10 | replicas: 1 11 | serviceName: "" 12 | selector: 13 | matchLabels: 14 | app.mayadata.io/name: hello-world 15 | template: 16 | metadata: 17 | labels: 18 | app.mayadata.io/name: hello-world 19 | spec: 20 | serviceAccountName: hello-world 21 | containers: 22 | - name: hello-world 23 | image: localhost:5000/hello-world:latest # local registry 24 | command: ["/hello-world"] 25 | args: 26 | - --logtostderr 27 | - --run-as-local 28 | - --workers-count=1 # number of workers per controller 29 | - --discovery-interval=40s 30 | - --cache-flush-interval=240s # re-sync interval 31 | - -v=5 32 | --- -------------------------------------------------------------------------------- /examples/gctl/hello-world/helloworld-rbac-crd.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: hello-world 5 | namespace: hello-world 6 | --- 7 | kind: ClusterRole 8 | apiVersion: rbac.authorization.k8s.io/v1beta1 9 | metadata: 10 | name: hello-world 11 | rules: 12 | - apiGroups: [""] 13 | resources: ["pods"] 14 | verbs: ["create", "delete", "update", "list"] 15 | - apiGroups: ["example.com"] 16 | resources: ["helloworlds"] 17 | verbs: ["get","watch", "update", "list"] 18 | --- 19 | kind: ClusterRoleBinding 20 | apiVersion: rbac.authorization.k8s.io/v1beta1 21 | metadata: 22 | name: hello-world 23 | subjects: 24 | - kind: ServiceAccount 25 | name: hello-world 26 | namespace: hello-world 27 | roleRef: 28 | kind: ClusterRole 29 | name: hello-world 30 | apiGroup: rbac.authorization.k8s.io 31 | --- 32 | apiVersion: apiextensions.k8s.io/v1beta1 33 | kind: CustomResourceDefinition 34 | metadata: 35 | name: helloworlds.example.com 36 | spec: 37 | group: example.com 38 | version: v1 39 | names: 40 | kind: HelloWorld 41 | plural: helloworlds 42 | singular: helloworld 43 | scope: Namespaced -------------------------------------------------------------------------------- /examples/gctl/hello-world/helloworld.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: example.com/v1 2 | kind: HelloWorld 3 | metadata: 4 | name: hello-amitd 5 | namespace: hello-world 6 | spec: 7 | who: amitd -------------------------------------------------------------------------------- /examples/gctl/hello-world/kind-with-registry.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -o errexit 3 | 4 | # desired cluster name; default is "kind" 5 | KIND_CLUSTER_NAME="${KIND_CLUSTER_NAME:-kind}" 6 | 7 | # create registry container unless it already exists 8 | reg_name='kind-registry' 9 | reg_port='5000' 10 | running="$(docker inspect -f '{{.State.Running}}' "${reg_name}" 2>/dev/null || true)" 11 | if [ "${running}" != 'true' ]; then 12 | docker run \ 13 | -d --restart=always -p "${reg_port}:5000" --name "${reg_name}" \ 14 | registry:2 15 | fi 16 | reg_ip="$(docker inspect -f '{{.NetworkSettings.IPAddress}}' "${reg_name}")" 17 | 18 | # create a cluster with the local registry enabled in containerd 19 | cat <. 10 | name: "storages.dao.amitd.io", 11 | }, 12 | spec: { 13 | // group name to use for REST API: /apis// 14 | group: "dao.amitd.io", 15 | // version name to use for REST API: /apis// 16 | version: "v1alpha1", 17 | // either Namespaced or Cluster 18 | scope: "Namespaced", 19 | names:{ 20 | // plural name to be used in the URL: 21 | // i.e. /apis/// 22 | plural: "storages", 23 | // # singular name to be used as an alias on the CLI and for display 24 | singular: "storage", 25 | // kind is normally the CamelCased singular type. 26 | // Your resource manifests use this. 27 | kind: "Storage", 28 | # shortNames allow shorter string to match your resource on the CLI 29 | shortNames: ["stor"], 30 | }, 31 | additionalPrinterColumns: [ 32 | { 33 | JSONPath: ".spec.capacity", 34 | name: "Capacity", 35 | description: "Capacity of the storage", 36 | type: "string", 37 | }, 38 | { 39 | JSONPath: ".spec.nodeName", 40 | name: "NodeName", 41 | description: "Node where the storage gets attached", 42 | type: "string", 43 | }, 44 | { 45 | JSONPath: ".status.phase", 46 | name: "Status", 47 | description: "Identifies the current status of the storage", 48 | type: "string", 49 | }, 50 | ], 51 | } 52 | } 53 | ] 54 | } 55 | -------------------------------------------------------------------------------- /examples/gctl/install-uninstall-crd/my-namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: amitd 5 | --- -------------------------------------------------------------------------------- /examples/gctl/install-uninstall-crd/operator.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: metac.openebs.io/v1alpha1 3 | kind: GenericController 4 | metadata: 5 | name: install-un-crd 6 | spec: 7 | watch: 8 | apiVersion: v1 9 | resource: namespaces 10 | nameSelector: 11 | # we are interested in amitd namespace only 12 | - amitd 13 | attachments: 14 | - apiVersion: apiextensions.k8s.io/v1beta1 15 | resource: customresourcedefinitions 16 | nameSelector: 17 | # we are interested in storages CRD only 18 | - storages.dao.amitd.io 19 | hooks: 20 | sync: 21 | webhook: 22 | url: http://jsonnetd.metac/sync-crd 23 | finalize: 24 | webhook: 25 | url: http://jsonnetd.metac/finalize-crd 26 | --- 27 | apiVersion: apps/v1 28 | kind: Deployment 29 | metadata: 30 | name: jsonnetd 31 | namespace: metac 32 | spec: 33 | replicas: 1 34 | selector: 35 | matchLabels: 36 | app: jsonnetd 37 | template: 38 | metadata: 39 | labels: 40 | app: jsonnetd 41 | spec: 42 | containers: 43 | - name: hooks 44 | # this deployment is all about exposing jsonnet as a webhook 45 | # that understands metac request & response payload 46 | image: metacontroller/jsonnetd:0.1 47 | imagePullPolicy: Always 48 | workingDir: /hooks 49 | volumeMounts: 50 | - name: hooks 51 | mountPath: /hooks 52 | volumes: 53 | - name: hooks 54 | configMap: 55 | # this configmap provides the jsonnet files that 56 | # get executed as webhooks 57 | name: install-uninstall-crd 58 | --- 59 | apiVersion: v1 60 | kind: Service 61 | metadata: 62 | # this name is used to build the webhook url 63 | name: jsonnetd 64 | # this namespace is used to build the webhook url 65 | namespace: metac 66 | spec: 67 | selector: 68 | app: jsonnetd 69 | ports: 70 | - port: 80 71 | targetPort: 8080 72 | -------------------------------------------------------------------------------- /examples/gctl/set-status-on-cr/.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | -------------------------------------------------------------------------------- /examples/gctl/set-status-on-cr/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.12.7 AS builder 2 | 3 | WORKDIR / 4 | 5 | # copy go modules manifests 6 | COPY go.mod go.mod 7 | COPY go.sum go.sum 8 | 9 | # ensure vendoring is up-to-date by running make vendor in your local 10 | # setup 11 | # 12 | # we cache the vendored dependencies before building and copying source 13 | # so that we don't need to re-download when source changes don't invalidate 14 | # our downloaded layer 15 | #RUN GO111MODULE=on go mod tidy 16 | RUN GO111MODULE=on go mod download 17 | RUN GO111MODULE=on go mod vendor 18 | 19 | # copy source files 20 | COPY *.go ./ 21 | 22 | RUN go build -o /go/bin/set-status-on-cr 23 | 24 | FROM debian:stretch-slim 25 | 26 | RUN apt-get update && \ 27 | apt-get install --no-install-recommends -y ca-certificates && \ 28 | rm -rf /var/lib/apt/lists/* 29 | 30 | COPY config/config.yaml /etc/config/metac/config.yaml 31 | COPY --from=builder /go/bin/set-status-on-cr /usr/bin/set-status-on-cr 32 | -------------------------------------------------------------------------------- /examples/gctl/set-status-on-cr/Makefile: -------------------------------------------------------------------------------- 1 | REGISTRY ?= quay.io/amitkumardas 2 | IMG_NAME ?= set-status-on-cr 3 | PACKAGE_VERSION ?= latest 4 | 5 | # go mod download modules to local cache 6 | # make vendored copy of dependencies 7 | # install other go binaries for code generation 8 | .PHONY: vendor 9 | vendor: go.mod go.sum 10 | @GO111MODULE=on go mod download 11 | @GO111MODULE=on go mod vendor 12 | 13 | .PHONY: image 14 | image: 15 | docker build -t $(REGISTRY)/$(IMG_NAME):$(PACKAGE_VERSION) . 16 | 17 | .PHONY: push 18 | push: image 19 | docker push $(REGISTRY)/$(IMG_NAME):$(PACKAGE_VERSION) -------------------------------------------------------------------------------- /examples/gctl/set-status-on-cr/README.md: -------------------------------------------------------------------------------- 1 | ## Set status on Custom Resource 2 | 3 | This is an example of a single binary Kubernetes controller. The controller logic is implemented in Go and packaged in a container. This controller will wait for the `CoolNerd` custom resource in $NAMESPACE to be created. Once created, it will update the CR Status. In this case, go code and config are the main files to be understood. 4 | 5 | ### Prerequisites 6 | 7 | * Kubernetes 1.8+ is recommended for its improved CRD support, especially garbage collection. 8 | * Install following yamls 9 | 10 | ```sh 11 | kubectl apply -f ns_and_crd.yaml 12 | kubectl apply -f rbac.yaml 13 | kubectl apply -f operator.yaml 14 | 15 | # verify if above was installed properly 16 | kubectl get ns 17 | kubectl get crd 18 | kubectl get deployment -n set-status-on-cr 19 | ``` 20 | 21 | ### Create the Custom Resource 22 | 23 | ```sh 24 | kubectl apply -f coolnerd.yaml 25 | ``` 26 | 27 | Watch for the CR to get created with status 28 | 29 | ```sh 30 | kubectl get coolnerds -n set-status-on-cr -oyaml 31 | ``` 32 | 33 | ### Cleanup 34 | 35 | ```sh 36 | kubectl delete -f coolnerd.yaml 37 | kubectl delete -f operator.yaml 38 | kubectl delete -f rbac.yaml 39 | kubectl delete -f ns_and_crd.yaml 40 | ``` -------------------------------------------------------------------------------- /examples/gctl/set-status-on-cr/config/config.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: metac.openebs.io/v1alpha1 2 | kind: GenericController 3 | metadata: 4 | name: set-status-on-cr 5 | spec: 6 | watch: 7 | apiVersion: examples.metac.io/v1 8 | resource: coolnerds 9 | hooks: 10 | sync: 11 | inline: 12 | funcName: sync/cool-nerd -------------------------------------------------------------------------------- /examples/gctl/set-status-on-cr/coolnerd.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: examples.metac.io/v1 3 | kind: CoolNerd 4 | metadata: 5 | name: cool-status-on-me 6 | namespace: set-status-on-cr 7 | --- -------------------------------------------------------------------------------- /examples/gctl/set-status-on-cr/go.mod: -------------------------------------------------------------------------------- 1 | module openebs.io/set-status-on-ns 2 | 3 | go 1.12 4 | 5 | require openebs.io/metac v0.1.1-0.20191104084521-0065a1c6df80 6 | 7 | replace openebs.io/metac => github.com/AmitKumarDas/metac v0.1.1-0.20191104084521-0065a1c6df80 8 | -------------------------------------------------------------------------------- /examples/gctl/set-status-on-cr/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "openebs.io/metac/controller/generic" 5 | "openebs.io/metac/start" 6 | ) 7 | 8 | // sync implements the idempotent logic to get to the desired 9 | // state 10 | // 11 | // NOTE: 12 | // SyncHookRequest is the payload received as part of reconcile 13 | // request. Similarly, SyncHookResponse is the payload sent as a 14 | // response as part of reconcile request. 15 | // 16 | // NOTE: 17 | // SyncHookRequest has the resource that is under watch, whereas 18 | // Both SyncHookRequest & SyncHookResponse have the resources that 19 | // form the desired state w.r.t the watched resource. These 20 | // resources are known as attachments. 21 | func sync(req *generic.SyncHookRequest, resp *generic.SyncHookResponse) error { 22 | if resp == nil { 23 | resp = &generic.SyncHookResponse{} 24 | } 25 | // Compute status based on latest observed state. 26 | if req == nil || req.Watch == nil { 27 | resp.ResyncAfterSeconds = 2 28 | resp.SkipReconcile = true 29 | return nil 30 | } 31 | 32 | resp.Status = map[string]interface{}{ 33 | "phase": "Active", 34 | "conditions": []string{ 35 | "Golang", 36 | "InlineHook", 37 | "GCtl", 38 | }, 39 | } 40 | return nil 41 | } 42 | 43 | func main() { 44 | generic.AddToInlineRegistry("sync/cool-nerd", sync) 45 | start.Start() 46 | } 47 | -------------------------------------------------------------------------------- /examples/gctl/set-status-on-cr/ns_and_crd.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: set-status-on-cr 5 | --- 6 | apiVersion: apiextensions.k8s.io/v1beta1 7 | kind: CustomResourceDefinition 8 | metadata: 9 | name: coolnerds.examples.metac.io 10 | spec: 11 | group: examples.metac.io 12 | version: v1 13 | scope: Namespaced 14 | names: 15 | plural: coolnerds 16 | singular: coolnerd 17 | kind: CoolNerd 18 | --- -------------------------------------------------------------------------------- /examples/gctl/set-status-on-cr/operator.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: set-status-on-cr 6 | namespace: set-status-on-cr 7 | spec: 8 | replicas: 1 9 | selector: 10 | matchLabels: 11 | app: set-status-on-cr 12 | template: 13 | metadata: 14 | labels: 15 | app: set-status-on-cr 16 | spec: 17 | serviceAccountName: set-status-on-cr 18 | containers: 19 | - name: set-status-on-cr 20 | image: quay.io/amitkumardas/set-status-on-cr:latest 21 | command: ["/usr/bin/set-status-on-cr"] 22 | args: 23 | - --logtostderr 24 | - --run-as-local 25 | - -v=1 26 | - --discovery-interval=20s 27 | --- -------------------------------------------------------------------------------- /examples/gctl/set-status-on-cr/rbac.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: set-status-on-cr 5 | namespace: set-status-on-cr 6 | --- 7 | apiVersion: rbac.authorization.k8s.io/v1 8 | kind: ClusterRole 9 | metadata: 10 | name: set-status-on-cr 11 | rules: 12 | - apiGroups: 13 | - examples.metac.io 14 | resources: 15 | - coolnerds 16 | verbs: 17 | - get 18 | - list 19 | - update 20 | --- 21 | apiVersion: rbac.authorization.k8s.io/v1 22 | kind: ClusterRoleBinding 23 | metadata: 24 | name: set-status-on-cr 25 | subjects: 26 | - kind: ServiceAccount 27 | name: set-status-on-cr 28 | namespace: set-status-on-cr 29 | roleRef: 30 | kind: ClusterRole 31 | name: set-status-on-cr 32 | apiGroup: rbac.authorization.k8s.io 33 | --- -------------------------------------------------------------------------------- /examples/gctl/set-status-on-cr/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cleanup() { 4 | set +e 5 | 6 | echo "" 7 | 8 | echo "--------------------------" 9 | echo "++ Clean up started" 10 | echo "--------------------------" 11 | 12 | kubectl delete -f coolnerd.yaml || true 13 | kubectl delete -f operator.yaml || true 14 | kubectl delete -f rbac.yaml || true 15 | kubectl delete -f ns_and_crd.yaml || true 16 | 17 | echo "--------------------------" 18 | echo "++ Clean up completed" 19 | echo "--------------------------" 20 | } 21 | trap cleanup EXIT 22 | 23 | # Uncomment this if you want to run this script in debug mode 24 | #set -ex 25 | 26 | # my cr name 27 | my_res="cool-status-on-me" 28 | my_ns="set-status-on-cr" 29 | 30 | echo -e "\n++ Installing operator" 31 | kubectl apply -f ns_and_crd.yaml 32 | kubectl apply -f rbac.yaml 33 | kubectl apply -f operator.yaml 34 | echo -e "\n++ Installed operator successfully" 35 | 36 | 37 | echo -e "\n++ Applying custom resource that gets watched by metac - I" 38 | kubectl apply -f coolnerd.yaml 39 | echo -e "\n++ Applied custom resource successfully" 40 | 41 | echo -e "\n++Waiting for custom resource's status to be updated" 42 | until [[ "$(kubectl -n $my_ns get coolnerd $my_res -o 'jsonpath={.status.phase}')" == "Active" ]]; do sleep 1; done 43 | echo -e "\n++Custom resource's status updated successfully" 44 | 45 | echo -e "\n++ Deleting custom resource that is watched by metac" 46 | kubectl delete -f coolnerd.yaml 47 | echo -e "\n++ Deleted custom resource successfully" 48 | 49 | echo -e "\n++ Applying custom resource that gets watched by metac - II" 50 | kubectl apply -f coolnerd.yaml 51 | echo -e "\n++ Applied custom resource successfully" 52 | 53 | echo -e "\n++Waiting for custom resource's status to be updated" 54 | until [[ "$(kubectl -n $my_ns get coolnerd $my_res -o 'jsonpath={.status.phase}')" == "Active" ]]; do sleep 1; done 55 | echo -e "\n++Custom resource's status updated successfully" 56 | 57 | echo -e "\n++ Test set-status-on-cr completed successfully" -------------------------------------------------------------------------------- /examples/gctl/uninstall-openebs/.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | -------------------------------------------------------------------------------- /examples/gctl/uninstall-openebs/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.12.7 AS builder 2 | 3 | WORKDIR / 4 | 5 | # copy go modules manifests 6 | COPY go.mod go.mod 7 | COPY go.sum go.sum 8 | 9 | # ensure vendoring is up-to-date by running make vendor in your local 10 | # setup 11 | # 12 | # we cache the vendored dependencies before building and copying source 13 | # so that we don't need to re-download when source changes don't invalidate 14 | # our downloaded layer 15 | RUN GO111MODULE=on go mod download 16 | RUN GO111MODULE=on go mod vendor 17 | 18 | # copy source files 19 | COPY *.go ./ 20 | 21 | RUN go build -o /go/bin/uninstall-openebs 22 | 23 | FROM debian:stretch-slim 24 | 25 | RUN apt-get update && \ 26 | apt-get install --no-install-recommends -y ca-certificates && \ 27 | rm -rf /var/lib/apt/lists/* 28 | 29 | COPY --from=builder /go/bin/uninstall-openebs /usr/bin/uninstall-openebs 30 | -------------------------------------------------------------------------------- /examples/gctl/uninstall-openebs/Makefile: -------------------------------------------------------------------------------- 1 | REGISTRY ?= quay.io/amitkumardas 2 | IMG_NAME ?= uninstall-openebs 3 | PACKAGE_VERSION ?= latest 4 | 5 | # go mod download modules to local cache 6 | # make vendored copy of dependencies 7 | # install other go binaries for code generation 8 | .PHONY: vendor 9 | vendor: go.mod go.sum 10 | @GO111MODULE=on go mod download 11 | @GO111MODULE=on go mod vendor 12 | 13 | .PHONY: image 14 | image: 15 | docker build -t $(REGISTRY)/$(IMG_NAME):$(PACKAGE_VERSION) . 16 | 17 | .PHONY: push 18 | push: image 19 | docker push $(REGISTRY)/$(IMG_NAME):$(PACKAGE_VERSION) -------------------------------------------------------------------------------- /examples/gctl/uninstall-openebs/go.mod: -------------------------------------------------------------------------------- 1 | module openebs.io/uninstall-openebs 2 | 3 | go 1.12 4 | 5 | require openebs.io/metac v0.1.1-0.20191112035947-4ea7d563defc 6 | 7 | replace openebs.io/metac => github.com/AmitKumarDas/metac v0.1.1-0.20191112035947-4ea7d563defc 8 | -------------------------------------------------------------------------------- /examples/gctl/uninstall-openebs/openebs_ns.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # This has been copied from following link: 3 | # https://github.com/openebs/openebs/blob/master/k8s/openebs-operator.yaml 4 | # 5 | # Create the OpenEBS namespace 6 | apiVersion: v1 7 | kind: Namespace 8 | metadata: 9 | name: openebs 10 | --- -------------------------------------------------------------------------------- /examples/gctl/uninstall-openebs/operator.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: uninstall-openebs 6 | namespace: uninstall-openebs 7 | spec: 8 | replicas: 1 9 | selector: 10 | matchLabels: 11 | app: uninstall-openebs 12 | template: 13 | metadata: 14 | labels: 15 | app: uninstall-openebs 16 | spec: 17 | serviceAccountName: uninstall-openebs 18 | containers: 19 | - name: uninstall-openebs 20 | image: quay.io/amitkumardas/uninstall-openebs:latest 21 | command: ["/usr/bin/uninstall-openebs"] 22 | args: 23 | - --logtostderr 24 | - --run-as-local 25 | - -v=4 26 | - --discovery-interval=20s 27 | volumeMounts: 28 | - name: config 29 | mountPath: /etc/config/metac 30 | volumes: 31 | - name: config 32 | configMap: 33 | # configmap provides the config file that 34 | # is used by this controller binary 35 | name: uninstall-openebs 36 | --- -------------------------------------------------------------------------------- /examples/gctl/uninstall-openebs/operator_ns.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Namespace 4 | metadata: 5 | name: uninstall-openebs 6 | --- -------------------------------------------------------------------------------- /examples/gctl/uninstall-openebs/operator_rbac.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: uninstall-openebs 5 | namespace: uninstall-openebs 6 | --- 7 | apiVersion: rbac.authorization.k8s.io/v1 8 | kind: ClusterRole 9 | metadata: 10 | name: uninstall-openebs 11 | rules: 12 | - apiGroups: 13 | - "openebs.io" 14 | resources: 15 | - "*" 16 | verbs: 17 | - "*" 18 | - apiGroups: 19 | - "" 20 | resources: 21 | - namespaces 22 | verbs: 23 | - "*" 24 | - apiGroups: 25 | - "apiextensions.k8s.io" 26 | resources: 27 | - customresourcedefinitions 28 | verbs: 29 | - "*" 30 | --- 31 | apiVersion: rbac.authorization.k8s.io/v1 32 | kind: ClusterRoleBinding 33 | metadata: 34 | name: uninstall-openebs 35 | subjects: 36 | - kind: ServiceAccount 37 | name: uninstall-openebs 38 | namespace: uninstall-openebs 39 | roleRef: 40 | kind: ClusterRole 41 | name: uninstall-openebs 42 | apiGroup: rbac.authorization.k8s.io 43 | --- -------------------------------------------------------------------------------- /examples/go/.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | -------------------------------------------------------------------------------- /examples/go/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.11 AS build 2 | 3 | COPY . /go/src/thing-controller 4 | WORKDIR /go/src/thing-controller 5 | RUN go get -u github.com/golang/dep/cmd/dep && dep ensure && go build -o /go/bin/thing-controller 6 | 7 | FROM debian:stretch-slim 8 | 9 | COPY --from=build /go/bin/thing-controller /usr/bin/thing-controller 10 | -------------------------------------------------------------------------------- /examples/go/Gopkg.toml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmitKumarDas/metac/d76885741b8d15c84f9b837dda567e983a43da3d/examples/go/Gopkg.toml -------------------------------------------------------------------------------- /examples/go/README.md: -------------------------------------------------------------------------------- 1 | ## Example Go Controller 2 | 3 | This controller doesn't do anything useful. 4 | It's just an example skeleton for writing Metacontroller hooks with Go. 5 | 6 | **WARNING** 7 | 8 | There's a [known issue](https://github.com/GoogleCloudPlatform/metacontroller/issues/76) 9 | that makes it difficult to produce JSON according to the rules that Metacontroller 10 | requires if you import the official Go structs for Kubernetes APIs. 11 | In particular, some fields will always be emitted, even if you never set them, 12 | which goes against Metacontroller's [apply semantics](https://metacontroller.app/api/apply/). 13 | 14 | ### Prerequisites 15 | 16 | * [Install Metacontroller](https://metacontroller.app/guide/install/) 17 | 18 | ### Install Thing Controller 19 | 20 | ```sh 21 | kubectl apply -f thing-controller.yaml 22 | ``` 23 | 24 | ### Create a Thing 25 | 26 | ```sh 27 | kubectl apply -f my-thing.yaml 28 | ``` 29 | 30 | Look at the thing: 31 | 32 | ```sh 33 | kubectl get thing -o yaml 34 | ``` 35 | 36 | Look at the thing the thing created: 37 | 38 | ```sh 39 | kubectl get pod thing-1 -a 40 | ``` 41 | 42 | Look at what the thing the thing created said: 43 | 44 | ```sh 45 | kubectl logs thing-1 46 | ``` 47 | 48 | ### Clean up 49 | 50 | ```sh 51 | kubectl delete -f thing-controller.yaml 52 | ``` 53 | 54 | ### Building 55 | 56 | You don't need to build to run the example above, 57 | but if you make changes: 58 | 59 | ```sh 60 | go get -u github.com/golang/dep/cmd/dep 61 | dep ensure 62 | go build -o thing-controller 63 | ``` 64 | 65 | Or just make a new container image: 66 | 67 | ```sh 68 | docker build . -t /thing-controller 69 | ``` 70 | -------------------------------------------------------------------------------- /examples/go/my-thing.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: examples.metac.io/v1 2 | kind: Thing 3 | metadata: 4 | name: thing-1 5 | spec: 6 | message: Hello, World! 7 | -------------------------------------------------------------------------------- /examples/go/thing-controller.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1beta1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | name: things.examples.metac.io 5 | spec: 6 | group: examples.metac.io 7 | version: v1 8 | scope: Namespaced 9 | names: 10 | plural: things 11 | singular: thing 12 | kind: Thing 13 | --- 14 | apiVersion: v1 15 | kind: Namespace 16 | metadata: 17 | name: metacontroller 18 | --- 19 | apiVersion: metac.openebs.io/v1alpha1 20 | kind: CompositeController 21 | metadata: 22 | name: thing-controller 23 | spec: 24 | generateSelector: true 25 | parentResource: 26 | apiVersion: examples.metac.io/v1 27 | resource: things 28 | childResources: 29 | - apiVersion: v1 30 | resource: pods 31 | hooks: 32 | sync: 33 | webhook: 34 | url: http://thing-controller.metacontroller/sync 35 | --- 36 | apiVersion: apps/v1 37 | kind: Deployment 38 | metadata: 39 | name: thing-controller 40 | namespace: metacontroller 41 | spec: 42 | replicas: 1 43 | selector: 44 | matchLabels: 45 | app: thing-controller 46 | template: 47 | metadata: 48 | labels: 49 | app: thing-controller 50 | spec: 51 | containers: 52 | - name: controller 53 | image: quay.io/amitkumardas/thing-controller:latest 54 | command: ["thing-controller"] 55 | --- 56 | apiVersion: v1 57 | kind: Service 58 | metadata: 59 | name: thing-controller 60 | namespace: metacontroller 61 | spec: 62 | selector: 63 | app: thing-controller 64 | ports: 65 | - port: 80 66 | targetPort: 8080 67 | -------------------------------------------------------------------------------- /examples/indexedjob/README.md: -------------------------------------------------------------------------------- 1 | ## IndexedJob 2 | 3 | This is an example CompositeController that's similar to Job, 4 | except that each Pod gets assigned a unique index, similar to StatefulSet. 5 | 6 | ### Prerequisites 7 | 8 | * Install [Metacontroller](https://github.com/GoogleCloudPlatform/metacontroller) 9 | 10 | ### Deploy the controller 11 | 12 | ```sh 13 | kubectl create configmap indexedjob-controller -n metacontroller --from-file=sync.py 14 | kubectl apply -f indexedjob-controller.yaml 15 | ``` 16 | 17 | ### Create an IndexedJob 18 | 19 | ```sh 20 | kubectl apply -f my-indexedjob.yaml 21 | ``` 22 | 23 | Each Pod created should print its index: 24 | 25 | ```console 26 | $ kubectl logs print-index-2 27 | 2 28 | ``` 29 | 30 | ### Failure Policy 31 | 32 | Implementing `activeDeadlineSeconds` and `backoffLimit` is left as an exercise for the reader. 33 | -------------------------------------------------------------------------------- /examples/indexedjob/indexedjob-controller.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1beta1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | name: indexedjobs.ctl.enisoc.com 5 | spec: 6 | group: ctl.enisoc.com 7 | version: v1 8 | scope: Namespaced 9 | names: 10 | plural: indexedjobs 11 | singular: indexedjob 12 | kind: IndexedJob 13 | shortNames: ["ij", "idxj"] 14 | subresources: 15 | status: {} 16 | --- 17 | apiVersion: metacontroller.k8s.io/v1alpha1 18 | kind: CompositeController 19 | metadata: 20 | name: indexedjob-controller 21 | spec: 22 | generateSelector: true 23 | parentResource: 24 | apiVersion: ctl.enisoc.com/v1 25 | resource: indexedjobs 26 | childResources: 27 | - apiVersion: v1 28 | resource: pods 29 | hooks: 30 | sync: 31 | webhook: 32 | url: http://indexedjob-controller.metacontroller/sync 33 | --- 34 | apiVersion: apps/v1beta1 35 | kind: Deployment 36 | metadata: 37 | name: indexedjob-controller 38 | namespace: metacontroller 39 | spec: 40 | replicas: 1 41 | selector: 42 | matchLabels: 43 | app: indexedjob-controller 44 | template: 45 | metadata: 46 | labels: 47 | app: indexedjob-controller 48 | spec: 49 | containers: 50 | - name: controller 51 | image: python:2.7 52 | command: ["python", "/hooks/sync.py"] 53 | volumeMounts: 54 | - name: hooks 55 | mountPath: /hooks 56 | volumes: 57 | - name: hooks 58 | configMap: 59 | name: indexedjob-controller 60 | --- 61 | apiVersion: v1 62 | kind: Service 63 | metadata: 64 | name: indexedjob-controller 65 | namespace: metacontroller 66 | spec: 67 | selector: 68 | app: indexedjob-controller 69 | ports: 70 | - port: 80 71 | -------------------------------------------------------------------------------- /examples/indexedjob/my-indexedjob.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: ctl.enisoc.com/v1 2 | kind: IndexedJob 3 | metadata: 4 | name: print-index 5 | spec: 6 | completions: 10 7 | parallelism: 2 8 | template: 9 | metadata: 10 | labels: 11 | app: print-index 12 | spec: 13 | restartPolicy: OnFailure 14 | containers: 15 | - name: print-index 16 | image: busybox 17 | command: ["sh", "-c", "echo ${JOB_INDEX}"] 18 | -------------------------------------------------------------------------------- /examples/indexedjob/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cleanup() { 4 | set +e 5 | echo "Clean up..." 6 | kubectl delete -f my-indexedjob.yaml 7 | kubectl delete po -l app=print-index 8 | kubectl delete -f indexedjob-controller.yaml 9 | kubectl delete configmap indexedjob-controller -n metacontroller 10 | } 11 | trap cleanup EXIT 12 | 13 | set -ex 14 | 15 | ij="indexedjobs" 16 | 17 | echo "Install controller..." 18 | kubectl create configmap indexedjob-controller -n metacontroller --from-file=sync.py 19 | kubectl apply -f indexedjob-controller.yaml 20 | 21 | echo "Wait until CRD is available..." 22 | until kubectl get $ij; do sleep 1; done 23 | 24 | echo "Create an object..." 25 | kubectl apply -f my-indexedjob.yaml 26 | 27 | echo "Wait for 10 successful completions..." 28 | until [[ "$(kubectl get $ij print-index -o 'jsonpath={.status.succeeded}')" -eq 10 ]]; do sleep 1; done 29 | 30 | echo "Check that correct index is printed..." 31 | if [[ "$(kubectl logs print-index-9)" != "9" ]]; then 32 | exit 1 33 | fi 34 | -------------------------------------------------------------------------------- /examples/jsonnetd/.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | /jsonnetd 3 | -------------------------------------------------------------------------------- /examples/jsonnetd/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.10 AS build 2 | 3 | RUN curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh 4 | 5 | COPY . /go/src/jsonnetd/ 6 | WORKDIR /go/src/jsonnetd/ 7 | RUN dep ensure && go install 8 | 9 | FROM debian:stretch-slim 10 | COPY --from=build /go/bin/jsonnetd /jsonnetd/ 11 | WORKDIR /jsonnetd 12 | ENTRYPOINT ["/jsonnetd/jsonnetd"] 13 | EXPOSE 8080 -------------------------------------------------------------------------------- /examples/jsonnetd/Gopkg.lock: -------------------------------------------------------------------------------- 1 | # This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. 2 | 3 | 4 | [[projects]] 5 | branch = "master" 6 | name = "github.com/google/go-jsonnet" 7 | packages = [".","ast","parser"] 8 | revision = "c7a5b68f1c7a2f5e69736e489790f58edd27f7f3" 9 | 10 | [solve-meta] 11 | analyzer-name = "dep" 12 | analyzer-version = 1 13 | inputs-digest = "dff655412715ccf4f74481ef79f3d20d587d66eb1e1b47aa715890f610302c6e" 14 | solver-name = "gps-cdcl" 15 | solver-version = 1 16 | -------------------------------------------------------------------------------- /examples/jsonnetd/Gopkg.toml: -------------------------------------------------------------------------------- 1 | 2 | # Gopkg.toml example 3 | # 4 | # Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md 5 | # for detailed Gopkg.toml documentation. 6 | # 7 | # required = ["github.com/user/thing/cmd/thing"] 8 | # ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] 9 | # 10 | # [[constraint]] 11 | # name = "github.com/user/project" 12 | # version = "1.0.0" 13 | # 14 | # [[constraint]] 15 | # name = "github.com/user/project2" 16 | # branch = "dev" 17 | # source = "github.com/myfork/project2" 18 | # 19 | # [[override]] 20 | # name = "github.com/x/y" 21 | # version = "2.4.0" 22 | 23 | -------------------------------------------------------------------------------- /examples/jsonnetd/Makefile: -------------------------------------------------------------------------------- 1 | tag = 0.1 2 | 3 | image: 4 | docker build -t metacontroller/jsonnetd:$(tag) . 5 | 6 | push: image 7 | docker push metacontroller/jsonnetd:$(tag) 8 | -------------------------------------------------------------------------------- /examples/jsonnetd/README.md: -------------------------------------------------------------------------------- 1 | ## jsonnetd 2 | 3 | This is an HTTP server that loads a directory of Jsonnet files and 4 | serves each one as a webhook. 5 | 6 | Each hook should evaluate to a Jsonnet function: 7 | 8 | ```js 9 | function(request) { 10 | // response body 11 | } 12 | ``` 13 | 14 | The body of the POST request is itself interpreted as Jsonnet 15 | and given to the hook as a top-level `request` argument. 16 | 17 | The entire result of evaluating the Jsonnet function is returned as 18 | the webhook response body, unless the function returns an error. 19 | -------------------------------------------------------------------------------- /examples/jsonnetd/extensions.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 Google 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 | https://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 main 18 | 19 | import ( 20 | "encoding/json" 21 | "fmt" 22 | "strconv" 23 | 24 | jsonnet "github.com/google/go-jsonnet" 25 | "github.com/google/go-jsonnet/ast" 26 | ) 27 | 28 | var extensions = []*jsonnet.NativeFunction{ 29 | // jsonUnmarshal adds a native function for unmarshaling JSON, 30 | // since there doesn't seem to be one in the standard library. 31 | { 32 | Name: "jsonUnmarshal", 33 | Params: ast.Identifiers{"jsonStr"}, 34 | Func: func(args []interface{}) (interface{}, error) { 35 | jsonStr, ok := args[0].(string) 36 | if !ok { 37 | return nil, fmt.Errorf("unexpected type %T for 'jsonStr' arg", args[0]) 38 | } 39 | val := make(map[string]interface{}) 40 | if err := json.Unmarshal([]byte(jsonStr), &val); err != nil { 41 | return nil, fmt.Errorf("can't unmarshal JSON: %v", err) 42 | } 43 | return val, nil 44 | }, 45 | }, 46 | 47 | // parseInt adds a native function for parsing non-decimal integers, 48 | // since there doesn't seem to be one in the standard library. 49 | { 50 | Name: "parseInt", 51 | Params: ast.Identifiers{"intStr", "base"}, 52 | Func: func(args []interface{}) (interface{}, error) { 53 | str, ok := args[0].(string) 54 | if !ok { 55 | return nil, fmt.Errorf("unexpected type %T for 'intStr' arg", args[0]) 56 | } 57 | base, ok := args[1].(float64) 58 | if !ok { 59 | return nil, fmt.Errorf("unexpected type %T for 'base' arg", args[1]) 60 | } 61 | intVal, err := strconv.ParseInt(str, int(base), 64) 62 | if err != nil { 63 | return nil, fmt.Errorf("can't parse 'intStr': %v", err) 64 | } 65 | return float64(intVal), nil 66 | }, 67 | }, 68 | } 69 | -------------------------------------------------------------------------------- /examples/kind-with-registry.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -o errexit 3 | 4 | # desired cluster name; default is "kind" 5 | KIND_CLUSTER_NAME="${KIND_CLUSTER_NAME:-kind}" 6 | 7 | # create registry container unless it already exists 8 | reg_name='kind-registry' 9 | reg_port='5000' 10 | running="$(docker inspect -f '{{.State.Running}}' "${reg_name}" 2>/dev/null || true)" 11 | if [ "${running}" != 'true' ]; then 12 | docker run \ 13 | -d --restart=always -p "${reg_port}:5000" --name "${reg_name}" \ 14 | registry:2 15 | fi 16 | reg_ip="$(docker inspect -f '{{.NetworkSettings.IPAddress}}' "${reg_name}")" 17 | 18 | # create a cluster with the local registry enabled in containerd 19 | cat < { 34 | let hook = hooks[request.url.split('/')[1]]; 35 | if (!hook) { 36 | response.writeHead(404, {'Content-Type': 'text/plain'}); 37 | response.end('Not found'); 38 | return; 39 | } 40 | 41 | // Read the whole request body. 42 | let body = []; 43 | request.on('error', (err) => { 44 | console.error(err); 45 | }).on('data', (chunk) => { 46 | body.push(chunk); 47 | }).on('end', () => { 48 | body = Buffer.concat(body).toString(); 49 | 50 | if (request.headers['content-type'] === 'application/json') { 51 | body = JSON.parse(body); 52 | } 53 | 54 | // Emulate part of the fission.io nodejs environment, 55 | // so we can use the same sync.js file. 56 | hook({request: {body: body}}).then((result) => { 57 | response.writeHead(result.status, result.headers); 58 | let body = result.body; 59 | if (typeof body !== 'string') { 60 | body = JSON.stringify(body); 61 | } 62 | response.end(body); 63 | }, (err) => { 64 | response.writeHead(500, {'Content-Type': 'text/plain'}); 65 | response.end(err.toString()); 66 | }); 67 | }); 68 | }).listen(8080); 69 | -------------------------------------------------------------------------------- /examples/sample-controller/js/README.md: -------------------------------------------------------------------------------- 1 | ## Sample Controller 2 | 3 | This is Metacontroller implementation of SampleController found at https://github.com/kubernetes/sample-controller/. 4 | 5 | Sample controller provides a simple implementation of Kubernetes extension (i.e. custom controller). It defines a custom resource of kind `Foo`. When an instance of Foo gets applied against Kubernetes cluster, it is expected to create a Deployment instance. There should be a deployment instance corresponding to each instance of Foo. 6 | 7 | This version of SampleController shows how a controller needs to be bother about constructing its desired state only. In other words, controller logic returns the desired 'Deployment' object from the observed Foo instance without bothering about inner details of Kubernetes. Metacontroller on its parts handles create, update & delete operations of the Deployment instance based on Foo instance's current state. 8 | 9 | This example also provides a fair idea to write idempotent logic required to implement Kubernetes controllers. 10 | 11 | ### Prerequisites 12 | 13 | * Install Metac from the repo's `manifests` folder 14 | ```sh 15 | kubectl apply -f ../../../manifests/ 16 | ``` 17 | 18 | ```sh 19 | # verify presence of metac CRDs 20 | kubectl get crd 21 | 22 | # verify presence of metac controller 23 | kubectl get sts -n metac 24 | ``` 25 | 26 | ### Deploy the sample controller 27 | 28 | ```sh 29 | # embed javascript controller logic into a configmap 30 | kubectl create configmap sample-controller -n metac --from-file=sync.js 31 | # apply NodeJS image that mounts above javascript controller code 32 | kubectl apply -f operator.yaml 33 | 34 | # verify presence of Foo CRD 35 | kubectl get crd | grep foos 36 | 37 | # verify presence of Foo controller deployment & service 38 | kubectl get deploy -n metac 39 | kubectl get svc -n metac 40 | ``` 41 | 42 | ### Create a Foo resource 43 | 44 | ```sh 45 | kubectl apply -f foo.yaml 46 | ``` 47 | 48 | ### Verify creation of resources 49 | ```sh 50 | kubectl get foo 51 | kubectl get deploy 52 | kubectl get po 53 | ``` 54 | 55 | ### Delete the Foo resource 56 | ```sh 57 | kubectl delete -f foo.yaml 58 | ``` 59 | 60 | ### Verify deletion of deployment object 61 | ```sh 62 | kubectl get foo 63 | kubectl get deploy 64 | kubectl get po 65 | ``` -------------------------------------------------------------------------------- /examples/sample-controller/js/foo.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: samplecontroller.example.io/v1alpha1 2 | kind: Foo 3 | metadata: 4 | name: myfoo 5 | labels: 6 | app: myfoo 7 | spec: 8 | deploymentName: my-deploy 9 | replicas: 3 -------------------------------------------------------------------------------- /examples/sample-controller/js/operator.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1beta1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | name: foos.samplecontroller.example.io 5 | spec: 6 | group: samplecontroller.example.io 7 | version: v1alpha1 8 | scope: Namespaced 9 | names: 10 | plural: foos 11 | singular: foo 12 | kind: Foo 13 | shortNames: 14 | - foo 15 | subresources: 16 | status: {} 17 | --- 18 | apiVersion: metac.openebs.io/v1alpha1 19 | kind: CompositeController 20 | metadata: 21 | name: sample-controller 22 | spec: 23 | generateSelector: true 24 | parentResource: 25 | apiVersion: samplecontroller.example.io/v1alpha1 26 | resource: foos 27 | childResources: 28 | - apiVersion: apps/v1 29 | resource: deployments 30 | updateStrategy: 31 | method: InPlace 32 | hooks: 33 | sync: 34 | webhook: 35 | url: http://sample-controller.metac/sync 36 | --- 37 | apiVersion: apps/v1 38 | kind: Deployment 39 | metadata: 40 | name: sample-controller 41 | namespace: metac 42 | spec: 43 | replicas: 1 44 | selector: 45 | matchLabels: 46 | app: sample-controller 47 | template: 48 | metadata: 49 | labels: 50 | app: sample-controller 51 | spec: 52 | containers: 53 | - name: controller 54 | image: metacontroller/nodejs-server:0.1 55 | imagePullPolicy: Always 56 | volumeMounts: 57 | - name: hooks 58 | mountPath: /node/hooks 59 | volumes: 60 | - name: hooks 61 | configMap: 62 | name: sample-controller 63 | --- 64 | apiVersion: v1 65 | kind: Service 66 | metadata: 67 | name: sample-controller 68 | namespace: metac 69 | spec: 70 | selector: 71 | app: sample-controller 72 | ports: 73 | - port: 80 74 | -------------------------------------------------------------------------------- /examples/sample-controller/js/sync.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google 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 | https://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 | var desiredDeployment = function (foo) { 18 | let lbls = { 19 | app: 'nginx', 20 | controller: foo.metadata.name 21 | } 22 | let deploy = { 23 | apiVersion: 'apps/v1', 24 | kind: 'Deployment', 25 | metadata: { 26 | name: foo.spec.deploymentName, 27 | namespace: foo.metadata.namespace 28 | }, 29 | spec: { 30 | replicas: foo.spec.replicas, 31 | selector: { 32 | matchLabels: lbls 33 | }, 34 | template: { 35 | metadata: { 36 | labels: lbls 37 | }, 38 | spec: { 39 | containers: [ 40 | { 41 | name: 'nginx', 42 | image: 'nginx:latest' 43 | } 44 | ] 45 | } 46 | } 47 | } 48 | }; 49 | return deploy; 50 | }; 51 | 52 | module.exports = async function (context) { 53 | let observed = context.request.body; 54 | let desired = {status: {}, children: []}; 55 | 56 | try { 57 | // observed foo object 58 | let foo = observed.parent; 59 | 60 | // extract available replicas from desired deployment if available 61 | let allDeploys = observed.children['Deployment.apps/v1']; 62 | let fooDeploy = allDeploys ? allDeploys[foo.spec.deploymentName] : null; 63 | let replicas = fooDeploy ? fooDeploy.status.availableReplicas : 0; 64 | 65 | // Set the status of Foo 66 | desired.status = { 67 | availableReplicas: replicas 68 | }; 69 | 70 | // Generate/Apply desired children 71 | desired.children = [ 72 | desiredDeployment(foo) 73 | ]; 74 | } catch (e) { 75 | return {status: 500, body: e.stack}; 76 | } 77 | 78 | return {status: 200, body: desired, headers: {'Content-Type': 'application/json'}}; 79 | }; 80 | -------------------------------------------------------------------------------- /examples/sample-controller/js/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cleanup() { 4 | set +e 5 | echo "" 6 | 7 | echo "--------------------------" 8 | echo "++ Clean up started" 9 | echo "--------------------------" 10 | 11 | kubectl delete -f foo.yaml || true 12 | kubectl delete -f operator.yaml || true 13 | kubectl delete configmap sample-controller -n metac || true 14 | 15 | echo "--------------------------" 16 | echo "++ Clean up completed" 17 | echo "--------------------------" 18 | } 19 | 20 | # Comment below if you want to check manually 21 | # the state of the cluster and intended resources 22 | trap cleanup EXIT 23 | 24 | # Uncomment below if debug / verbose execution is needed 25 | #set -ex 26 | 27 | my_crd="foos.samplecontroller.example.io" 28 | my_deploy="my-deploy" 29 | 30 | echo -e "\n Install SampleController..." 31 | kubectl create configmap sample-controller -n metac --from-file=sync.js 32 | kubectl apply -f operator.yaml 33 | 34 | echo -e "\n Wait until SampleController $my_crd is available..." 35 | until kubectl get $my_crd; do sleep 1; done 36 | 37 | echo -e "\n Create a Foo object..." 38 | kubectl apply -f foo.yaml 39 | 40 | echo -e "\n Wait for Foo deployment to be available..." 41 | until kubectl get deploy $my_deploy; do sleep 1; done 42 | 43 | echo -e "\n Wait for Foo deployment's pods to be available..." 44 | until [[ "$(kubectl get deploy $my_deploy -o 'jsonpath={.status.availableReplicas}')" -eq 3 ]]; do sleep 1; done 45 | 46 | echo -e "\n Delete the Foo object..." 47 | kubectl delete -f foo.yaml 48 | 49 | echo -e "\n++ Waiting for Foo Deployment deletion..." 50 | until [[ "$(kubectl get deploy $my_deploy 2>&1)" == *NotFound* ]]; do sleep 1; done -------------------------------------------------------------------------------- /examples/service-per-pod/hooks/finalize-service-per-pod.jsonnet: -------------------------------------------------------------------------------- 1 | function(request) { 2 | // If the StatefulSet is updated to no longer match our decorator selector, 3 | // or if the StatefulSet is deleted, clean up any attachments we made. 4 | attachments: [], 5 | // Mark as finalized once we observe all Services are gone. 6 | finalized: std.length(request.attachments['Service.v1']) == 0 7 | } 8 | -------------------------------------------------------------------------------- /examples/service-per-pod/hooks/sync-pod-name-label.jsonnet: -------------------------------------------------------------------------------- 1 | function(request) { 2 | local pod = request.object, 3 | local labelKey = pod.metadata.annotations["pod-name-label"], 4 | 5 | // Inject the Pod name as a label with the key requested in the annotation. 6 | labels: { 7 | [labelKey]: pod.metadata.name 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/service-per-pod/hooks/sync-service-per-pod.jsonnet: -------------------------------------------------------------------------------- 1 | function(request) { 2 | local statefulset = request.object, 3 | local labelKey = statefulset.metadata.annotations["service-per-pod-label"], 4 | local ports = statefulset.metadata.annotations["service-per-pod-ports"], 5 | 6 | // Create a service for each Pod, with a selector on the given label key. 7 | attachments: [ 8 | { 9 | apiVersion: "v1", 10 | kind: "Service", 11 | metadata: { 12 | name: statefulset.metadata.name + "-" + index, 13 | labels: {app: "service-per-pod"} 14 | }, 15 | spec: { 16 | selector: { 17 | [labelKey]: statefulset.metadata.name + "-" + index 18 | }, 19 | ports: [ 20 | { 21 | local parts = std.split(portnums, ":"), 22 | port: std.parseInt(parts[0]), 23 | targetPort: std.parseInt(parts[1]), 24 | } 25 | for portnums in std.split(ports, ",") 26 | ] 27 | } 28 | } 29 | for index in std.range(0, statefulset.spec.replicas - 1) 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /examples/service-per-pod/my-statefulset.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: nginx 5 | labels: 6 | app: nginx 7 | spec: 8 | ports: 9 | - port: 80 10 | name: web 11 | clusterIP: None 12 | selector: 13 | app: nginx 14 | --- 15 | apiVersion: apps/v1 16 | kind: StatefulSet 17 | metadata: 18 | name: nginx 19 | annotations: 20 | service-per-pod-label: pod-name 21 | service-per-pod-ports: "80:80" 22 | spec: 23 | selector: 24 | matchLabels: 25 | app: nginx 26 | serviceName: nginx 27 | replicas: 3 28 | template: 29 | metadata: 30 | labels: 31 | app: nginx 32 | annotations: 33 | pod-name-label: pod-name 34 | spec: 35 | terminationGracePeriodSeconds: 1 36 | containers: 37 | - name: nginx 38 | image: gcr.io/google_containers/nginx-slim:0.8 39 | ports: 40 | - containerPort: 80 41 | name: web 42 | -------------------------------------------------------------------------------- /examples/service-per-pod/operator.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: metac.openebs.io/v1alpha1 2 | kind: DecoratorController 3 | metadata: 4 | name: service-per-pod 5 | spec: 6 | resources: 7 | - apiVersion: apps/v1 8 | resource: statefulsets 9 | annotationSelector: 10 | matchExpressions: 11 | - {key: service-per-pod-label, operator: Exists} 12 | - {key: service-per-pod-ports, operator: Exists} 13 | attachments: 14 | - apiVersion: v1 15 | resource: services 16 | hooks: 17 | sync: 18 | webhook: 19 | url: http://jsonnetd.metac/sync-service-per-pod 20 | finalize: 21 | webhook: 22 | url: http://jsonnetd.metac/finalize-service-per-pod 23 | --- 24 | apiVersion: metac.openebs.io/v1alpha1 25 | kind: DecoratorController 26 | metadata: 27 | name: pod-name-label 28 | spec: 29 | resources: 30 | - apiVersion: v1 31 | resource: pods 32 | labelSelector: 33 | matchExpressions: 34 | - {key: pod-name, operator: DoesNotExist} 35 | annotationSelector: 36 | matchExpressions: 37 | - {key: pod-name-label, operator: Exists} 38 | hooks: 39 | sync: 40 | webhook: 41 | url: http://jsonnetd.metac/sync-pod-name-label 42 | --- 43 | apiVersion: apps/v1 44 | kind: Deployment 45 | metadata: 46 | name: jsonnetd 47 | namespace: metac 48 | spec: 49 | replicas: 1 50 | selector: 51 | matchLabels: 52 | app: jsonnetd 53 | template: 54 | metadata: 55 | labels: 56 | app: jsonnetd 57 | spec: 58 | containers: 59 | - name: hooks 60 | # this deployment is all about exposing jsonnet as a webhook 61 | # that understands metac request & response payload 62 | image: metacontroller/jsonnetd:0.1 63 | imagePullPolicy: Always 64 | workingDir: /hooks 65 | volumeMounts: 66 | - name: hooks 67 | mountPath: /hooks 68 | volumes: 69 | - name: hooks 70 | configMap: 71 | # this configmap provides the jsonnet files that 72 | # get executed as webhooks 73 | name: service-per-pod-hooks 74 | --- 75 | apiVersion: v1 76 | kind: Service 77 | metadata: 78 | # this name is used to build the webhook url 79 | name: jsonnetd 80 | # this namespace is used to build the webhook url 81 | namespace: metac 82 | spec: 83 | selector: 84 | app: jsonnetd 85 | ports: 86 | - port: 80 87 | targetPort: 8080 88 | -------------------------------------------------------------------------------- /examples/status/README.md: -------------------------------------------------------------------------------- 1 | ## Noop 2 | 3 | This is an example DecoratorController returning a status 4 | 5 | ### Prerequisites 6 | 7 | * Install Metac from the manifests folder 8 | 9 | ```sh 10 | # verify creation of CRDs 11 | kubectl get crd 12 | ``` 13 | 14 | ### Steps to deploy Noop controller 15 | 16 | ```sh 17 | # This configmap embeds the sync hook file (a javascript impl). 18 | # This hook file is injected into noop controller. 19 | # 20 | # NOTE: 21 | # Noop controller is a nodejs server exposing a http endpoint URL 22 | # ending with '/sync' 23 | kubectl create configmap noop-controller -n metacontroller --from-file=sync.js 24 | 25 | # Deploy the Noop controller 26 | kubectl apply -f operator.yaml 27 | 28 | # Verify creation of CRD 29 | kubectl get crd noops.metac.openebs.io -oyaml 30 | 31 | # Verify creation of Noop operator 32 | kubectl get deploy -n metac 33 | kubectl get svc -n metac 34 | kubectl get configmap -n metac 35 | ``` 36 | 37 | ### Verify by creating a Noop resource 38 | 39 | ```sh 40 | kubectl apply -f my-noop.yaml 41 | 42 | # verify if status is set with 'success' 43 | kubectl get noop -oyaml 44 | ``` 45 | -------------------------------------------------------------------------------- /examples/status/my-noop.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: metac.openebs.io/v1 2 | # Doesn't this sound great for creating your 3 | # own test controllers 4 | kind: Noop 5 | metadata: 6 | name: noop 7 | spec: 8 | -------------------------------------------------------------------------------- /examples/status/operator.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1beta1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | name: noops.metac.openebs.io 5 | spec: 6 | group: metac.openebs.io 7 | version: v1 8 | scope: Namespaced 9 | names: 10 | plural: noops 11 | singular: noop 12 | kind: Noop 13 | subresources: 14 | status: {} 15 | --- 16 | apiVersion: metac.openebs.io/v1alpha1 17 | kind: DecoratorController 18 | metadata: 19 | name: noop-controller 20 | spec: 21 | resources: 22 | # we are interested for noops resource only 23 | - apiVersion: metac.openebs.io/v1 24 | resource: noops 25 | hooks: 26 | sync: 27 | webhook: 28 | url: http://noop-controller.metac/sync 29 | --- 30 | apiVersion: apps/v1beta1 31 | kind: Deployment 32 | metadata: 33 | name: noop-controller 34 | namespace: metac 35 | spec: 36 | replicas: 1 37 | selector: 38 | matchLabels: 39 | app: noop-controller 40 | template: 41 | metadata: 42 | labels: 43 | app: noop-controller 44 | spec: 45 | containers: 46 | - name: controller 47 | image: metacontroller/nodejs-server:0.1 48 | imagePullPolicy: Always 49 | volumeMounts: 50 | - name: hooks 51 | mountPath: /node/hooks 52 | volumes: 53 | - name: hooks 54 | configMap: 55 | name: noop-controller 56 | 57 | --- 58 | apiVersion: v1 59 | kind: Service 60 | metadata: 61 | name: noop-controller 62 | namespace: metac 63 | spec: 64 | selector: 65 | app: noop-controller 66 | ports: 67 | - port: 80 68 | -------------------------------------------------------------------------------- /examples/status/sync.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | Copyright 2019 The MayaData Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | https://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | 19 | module.exports = async function (context) { 20 | return { 21 | status: 200, 22 | body: { 23 | // Noop resource will have this status set 24 | // after the sync operation 25 | status: { 26 | message: "success", 27 | }, 28 | }, 29 | headers: {'Content-Type': 'application/json'}, 30 | }; 31 | }; 32 | -------------------------------------------------------------------------------- /examples/status/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cleanup() { 4 | set +e 5 | echo "" 6 | 7 | echo "--------------------------" 8 | echo "++ Clean up started" 9 | echo "--------------------------" 10 | 11 | kubectl delete -f my-noop.yaml 12 | kubectl delete rs,svc -l app=noop-controller 13 | kubectl delete -f operator.yaml 14 | kubectl delete configmap noop-controller -n metac 15 | 16 | echo "--------------------------" 17 | echo "++ Clean up completed" 18 | echo "--------------------------" 19 | } 20 | # Comment below if you want to check manually 21 | # the state of the cluster and intended resources 22 | trap cleanup EXIT 23 | 24 | # Uncomment below if debug / verbose execution is needed 25 | #set -ex 26 | 27 | # noop crd name 28 | np="noops.metac.openebs.io" 29 | 30 | echo -e "\n++Will install Noop operator..." 31 | kubectl create configmap noop-controller -n metac --from-file=sync.js 32 | kubectl apply -f operator.yaml 33 | 34 | echo -e "\n++Wait until CRD is available..." 35 | until kubectl get $np; do sleep 1; done 36 | 37 | echo -e "\n++Will apply Noop resource..." 38 | kubectl apply -f my-noop.yaml 39 | 40 | echo -e "\n++Wait for Noop resource's status to be updated..." 41 | until [[ "$(kubectl get $np noop -o 'jsonpath={.status.message}')" == "success" ]]; do sleep 1; done 42 | -------------------------------------------------------------------------------- /examples/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script runs the smoke tests that check basic Metacontroller functionality 4 | # by running through each example controller. 5 | # 6 | # * You should only run this in a test cluster. 7 | # * You should already have Metacontroller installed in your test cluster. 8 | # * You should have kubectl in your PATH and configured for the right cluster. 9 | 10 | set -e 11 | 12 | logfile=$(mktemp) 13 | echo "Logging test output to ${logfile}" 14 | 15 | cleanup() { 16 | rm ${logfile} 17 | } 18 | trap cleanup EXIT 19 | 20 | for test in */test.sh; do 21 | echo -n "Running ${test}..." 22 | if ! (cd "$(dirname "${test}")" && ./test.sh > ${logfile} 2>&1); then 23 | echo "FAILED" 24 | cat ${logfile} 25 | echo "Test ${test} failed!" 26 | exit 1 27 | fi 28 | echo "PASSED" 29 | done 30 | -------------------------------------------------------------------------------- /examples/vitess/README.md: -------------------------------------------------------------------------------- 1 | ## Vitess Operator 2 | 3 | **NOTE: The [Vitess Operator][] has moved to its own repository, 4 | and is now maintained by the Vitess project.** 5 | 6 | [Vitess Operator]: https://github.com/vitessio/vitess-operator 7 | 8 | This is an example of an app-specific [Operator](https://coreos.com/operators/), 9 | in this case for [Vitess](http://vitess.io), built with Metacontroller. 10 | 11 | It's meant to demonstrate the following patterns: 12 | 13 | * Building an Operator for a complex, stateful application out of a set of small 14 | Lambda Controllers that each do one thing well. 15 | * In addition to presenting a k8s-style API to users, this Operator uses 16 | custom k8s API objects to coordinate within itself. 17 | * Each controller manages one layer of the hierarchical Vitess cluster topology. 18 | The user only needs to create and manage a single, top-level VitessCluster 19 | object. 20 | * Replacing static, client-side template rendering with Lambda Controllers, 21 | which can adjust based on dynamic cluster state. 22 | * Each controller aggregates status and orchestrates app-specific rolling 23 | updates for its immediate children. 24 | * The top-level object contains a continuously-updated, aggregate "Ready" 25 | condition for the whole app, and can be directly edited to trigger rolling 26 | updates throughout the app. 27 | * Using a functional-style language ([Jsonnet](http://jsonnet.org)) to 28 | define Lambda Controllers in terms of template-like transformations on JSON 29 | objects. 30 | * You can use any language to write a Lambda Controller webhook, but the 31 | functional style is a good fit for a process that conceptually consists of 32 | declarative input, declarative output, and no side effects. 33 | * As a JSON templating language, Jsonnet is a particularly good fit for 34 | generating k8s manifests, providing functionality missing from pure 35 | JavaScript, such as first-class *merge* and *deep equal* operations. 36 | * Using the "Apply" update strategy feature of CompositeController, which 37 | emulates the behavior of `kubectl apply`, except that it attempts to do 38 | pseudo-strategic merges for CRDs. 39 | 40 | See the [Vitess Operator][] repository for details. 41 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module openebs.io/metac 2 | 3 | // This denotes the minimum supported language version and 4 | // should not include the patch version. 5 | go 1.13 6 | 7 | require ( 8 | contrib.go.opencensus.io/exporter/prometheus v0.1.0 9 | github.com/evanphx/json-patch v4.5.0+incompatible // indirect 10 | github.com/ghodss/yaml v1.0.0 11 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b 12 | github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef // indirect 13 | github.com/google/go-cmp v0.3.0 14 | github.com/google/go-jsonnet v0.14.0 15 | github.com/googleapis/gnostic v0.3.1 // indirect 16 | github.com/imdario/mergo v0.3.6 // indirect 17 | github.com/onsi/ginkgo v1.11.0 // indirect 18 | github.com/onsi/gomega v1.8.1 19 | github.com/pkg/errors v0.8.1 20 | go.opencensus.io v0.21.0 21 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 // indirect 22 | golang.org/x/sys v0.0.0-20190922100055-0a153f010e69 // indirect 23 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect 24 | gopkg.in/yaml.v2 v2.2.7 25 | k8s.io/api v0.17.0 26 | k8s.io/apiextensions-apiserver v0.17.0 27 | k8s.io/apimachinery v0.17.0 28 | k8s.io/client-go v0.17.0 29 | k8s.io/code-generator v0.17.0 30 | k8s.io/klog v1.0.0 31 | sigs.k8s.io/controller-tools v0.2.4 32 | ) 33 | 34 | replace ( 35 | k8s.io/api => k8s.io/api v0.17.0 36 | k8s.io/apimachinery => k8s.io/apimachinery v0.17.0 37 | k8s.io/client-go => k8s.io/client-go v0.17.0 38 | k8s.io/code-generator => k8s.io/code-generator v0.17.0 39 | ) 40 | -------------------------------------------------------------------------------- /hack/custom-boilerplate.go.txt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 The MayaData Authors 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 | -------------------------------------------------------------------------------- /hack/get-kube-binaries.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | set -u 5 | 6 | echo "" 7 | echo "+++ Will install binaries required to run integration test(s)" 8 | echo "" 9 | 10 | # This script downloads kubectl, kube-apiserver & etcd binaries 11 | # that are used as part of the integration test environment, 12 | # and places them in the following path: 13 | # 14 | # test/integration/framework/assets/bin 15 | # 16 | # Integration test framework expects these binaries to be 17 | # found in the PATH. 18 | 19 | # This is the kube-apiserver version to test against. 20 | KUBE_VERSION="${KUBE_VERSION:-v1.16.4}" 21 | KUBERNETES_RELEASE_URL="${KUBERNETES_RELEASE_URL:-https://dl.k8s.io}" 22 | 23 | # This should be the etcd version downloaded by 24 | # kubernetes/hack/lib/etcd.sh as of the above Kubernetes version. 25 | ETCD_VERSION="${ETCD_VERSION:-v3.4.3}" 26 | 27 | # This is the folder where binaries will be downloaded 28 | cd test/integration/framework/assets/bin 29 | 30 | # Download kubectl if not found 31 | if [[ -f ./kubectl ]] && ./kubectl version --client; then 32 | echo "" 33 | echo "+++ Above kubectl was installed previously" 34 | echo "" 35 | else 36 | wget -nv "${KUBERNETES_RELEASE_URL}/${KUBE_VERSION}/bin/linux/amd64/kubectl" 37 | chmod +x kubectl 38 | fi 39 | 40 | 41 | # Download kube-apiserver if not found 42 | if [[ -f ./kube-apiserver ]] && ./kube-apiserver --version; then 43 | echo "" 44 | echo "+++ Above kube-apiserver was installed previously" 45 | echo "" 46 | else 47 | wget -nv "${KUBERNETES_RELEASE_URL}/${KUBE_VERSION}/bin/linux/amd64/kube-apiserver" 48 | chmod +x kube-apiserver 49 | fi 50 | 51 | # Download etcd if not found 52 | if [[ -f ./etcd ]] && ./etcd --version; then 53 | echo "" 54 | echo "+++ Above etcd was installed previously" 55 | echo "" 56 | else 57 | basename="etcd-${ETCD_VERSION}-linux-amd64" 58 | filename="${basename}.tar.gz" 59 | url="https://github.com/coreos/etcd/releases/download/${ETCD_VERSION}/${filename}" 60 | wget -nv "${url}" 61 | tar -zxf "${filename}" 62 | mv "${basename}/etcd" etcd 63 | rm -rf "${basename}" "${filename}" 64 | fi 65 | -------------------------------------------------------------------------------- /hack/tools.go: -------------------------------------------------------------------------------- 1 | // +build tools 2 | 3 | /* 4 | Copyright 2019 The Kubernetes Authors. 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | */ 18 | 19 | // This package imports things required by build scripts, to force `go mod` to see them as dependencies 20 | package tools 21 | 22 | import ( 23 | _ "k8s.io/code-generator" 24 | _ "k8s.io/code-generator/cmd/deepcopy-gen" 25 | _ "sigs.k8s.io/controller-tools/cmd/controller-gen" 26 | ) 27 | -------------------------------------------------------------------------------- /hack/update-codegen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2017 The Kubernetes Authors. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | set -o errexit 18 | set -o nounset 19 | set -o pipefail 20 | 21 | SCRIPT_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. 22 | CODEGEN_PKG=${CODEGEN_PKG:-$(cd "${SCRIPT_ROOT}"; ls -d -1 ./vendor/k8s.io/code-generator 2>/dev/null || echo $GOPATH/src/k8s.io/code-generator)} 23 | 24 | chmod +x ${CODEGEN_PKG}/*.sh 25 | 26 | # generate the code with: 27 | # --output-base because this script should also be able to run inside the vendor dir of 28 | # k8s.io/kubernetes. The output-base is needed for the generators to output into the vendor dir 29 | # instead of the $GOPATH directly. For normal projects this can be dropped. 30 | eval "${CODEGEN_PKG}"/generate-groups.sh "deepcopy,client,informer,lister" \ 31 | openebs.io/metac/client/generated openebs.io/metac/apis \ 32 | metacontroller:v1alpha1 \ 33 | --output-base "$(dirname "${BASH_SOURCE[0]}")/../../.." \ 34 | --go-header-file "${SCRIPT_ROOT}"/hack/custom-boilerplate.go.txt 35 | 36 | # To use your own boilerplate text append: 37 | # --go-header-file "${SCRIPT_ROOT}"/hack/custom-boilerplate.go.txt 38 | -------------------------------------------------------------------------------- /hack/verify-codegen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2017 The Kubernetes Authors. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | set -o errexit 18 | set -o nounset 19 | set -o pipefail 20 | 21 | SCRIPT_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. 22 | 23 | DIFFROOT="${SCRIPT_ROOT}/pkg" 24 | TMP_DIFFROOT="${SCRIPT_ROOT}/_tmp/pkg" 25 | _tmp="${SCRIPT_ROOT}/_tmp" 26 | 27 | cleanup() { 28 | rm -rf "${_tmp}" 29 | } 30 | trap "cleanup" EXIT SIGINT 31 | 32 | cleanup 33 | 34 | mkdir -p "${TMP_DIFFROOT}" 35 | cp -a "${DIFFROOT}"/* "${TMP_DIFFROOT}" 36 | 37 | "${SCRIPT_ROOT}/hack/update-codegen.sh" 38 | echo "diffing ${DIFFROOT} against freshly generated codegen" 39 | ret=0 40 | diff -Naupr "${DIFFROOT}" "${TMP_DIFFROOT}" || ret=$? 41 | cp -a "${TMP_DIFFROOT}"/* "${DIFFROOT}" 42 | if [[ $ret -eq 0 ]] 43 | then 44 | echo "${DIFFROOT} up to date." 45 | else 46 | echo "${DIFFROOT} is out of date. Please run hack/update-codegen.sh" 47 | exit 1 48 | fi 49 | -------------------------------------------------------------------------------- /helm/metac/Chart.yaml: -------------------------------------------------------------------------------- 1 | name: metac 2 | apiVersion: v1 3 | appVersion: "v0.1.0" 4 | description: A Helm chart for Metac 5 | version: 0.1.0 6 | home: https://github.com/AmitKumarDas/metac 7 | keywords: 8 | - CRDs 9 | - metac 10 | - operator 11 | - controller 12 | - framework 13 | - metacontroller 14 | sources: 15 | - https://github.com/AmitKumarDas/metac 16 | -------------------------------------------------------------------------------- /helm/metac/crds/metac.openebs.io_controllerrevisions.yaml: -------------------------------------------------------------------------------- 1 | 2 | --- 3 | apiVersion: apiextensions.k8s.io/v1beta1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | annotations: 7 | "helm.sh/hook": crd-install 8 | controller-gen.kubebuilder.io/version: (devel) 9 | creationTimestamp: null 10 | name: controllerrevisions.metac.openebs.io 11 | spec: 12 | group: metac.openebs.io 13 | names: 14 | kind: ControllerRevision 15 | listKind: ControllerRevisionList 16 | plural: controllerrevisions 17 | singular: controllerrevision 18 | scope: Namespaced 19 | subresources: 20 | status: {} 21 | validation: 22 | openAPIV3Schema: 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 | children: 30 | items: 31 | properties: 32 | apiGroup: 33 | type: string 34 | kind: 35 | type: string 36 | names: 37 | items: 38 | type: string 39 | type: array 40 | required: 41 | - apiGroup 42 | - kind 43 | - names 44 | type: object 45 | type: array 46 | kind: 47 | description: 'Kind is a string value representing the REST resource this 48 | object represents. Servers may infer this from the endpoint the client 49 | submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' 50 | type: string 51 | metadata: 52 | type: object 53 | parentPatch: 54 | type: object 55 | required: 56 | - metadata 57 | - parentPatch 58 | type: object 59 | version: v1alpha1 60 | versions: 61 | - name: v1alpha1 62 | served: true 63 | storage: true 64 | status: 65 | acceptedNames: 66 | kind: "" 67 | plural: "" 68 | conditions: [] 69 | storedVersions: [] 70 | -------------------------------------------------------------------------------- /helm/metac/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | Congratulations on your new installation of Metac! 2 | 3 | Verify metac is talking properly to the Kubernetes API servers by running the following: 4 | 5 | kubectl logs -f {{ template "metac.fullname" . }}-0 6 | -------------------------------------------------------------------------------- /helm/metac/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=mustache: */}} 2 | {{/* 3 | Expand the name of the chart. 4 | */}} 5 | {{- define "metac.name" -}} 6 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} 7 | {{- end -}} 8 | 9 | {{/* 10 | Create a default fully qualified app name. 11 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 12 | If release name contains chart name it will be used as a full name. 13 | */}} 14 | {{- define "metac.fullname" -}} 15 | {{- if .Values.fullnameOverride -}} 16 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} 17 | {{- else -}} 18 | {{- $name := default .Chart.Name .Values.nameOverride -}} 19 | {{- if contains $name .Release.Name -}} 20 | {{- .Release.Name | trunc 63 | trimSuffix "-" -}} 21 | {{- else -}} 22 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} 23 | {{- end -}} 24 | {{- end -}} 25 | {{- end -}} 26 | 27 | {{/* 28 | Create chart name and version as used by the chart label. 29 | */}} 30 | {{- define "metac.chart" -}} 31 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} 32 | {{- end -}} 33 | 34 | {{- define "metac.serviceAccountName" -}} 35 | {{- if .Values.serviceAccount.create -}} 36 | {{ default (include "metac.fullname" .) .Values.serviceAccount.name }} 37 | {{- else -}} 38 | {{ default "default" .Values.serviceAccount.name }} 39 | {{- end -}} 40 | {{- end -}} 41 | 42 | {{/* Generate basic labels */}} 43 | {{- define "metac.labels" }} 44 | chart: {{ .Chart.Name }}-{{ .Chart.Version }} 45 | release: {{ .Release.Name | quote }} 46 | heritage: {{ .Release.Service | quote }} 47 | {{- end }} 48 | -------------------------------------------------------------------------------- /helm/metac/templates/crd-cleanup-job.yaml: -------------------------------------------------------------------------------- 1 | {{ if .Values.crds.cleanup }} 2 | apiVersion: batch/v1 3 | kind: Job 4 | metadata: 5 | name: {{ template "metac.fullname" . }}-crd-cleanup 6 | namespace: {{ .Release.Namespace }} 7 | annotations: 8 | "helm.sh/hook": post-delete 9 | "helm.sh/hook-weight": "3" 10 | "helm.sh/hook-delete-policy": hook-succeeded 11 | labels: 12 | app: {{ template "metac.name" . }}-crd-cleanup 13 | {{- include "metac.labels" . | nindent 4 }} 14 | 15 | spec: 16 | template: 17 | metadata: 18 | name: {{ template "metac.fullname" . }}-crd-cleanup 19 | labels: 20 | app: {{ template "metac.fullname" . }}-crd-cleanup 21 | {{- include "metac.labels" . | nindent 8 }} 22 | spec: 23 | {{- if .Values.rbac.create }} 24 | serviceAccountName: {{ template "metac.serviceAccountName" . }} 25 | {{- end }} 26 | containers: 27 | - name: kubectl 28 | image: "{{ .Values.hyperkubeImage.repository }}:{{ .Values.hyperkubeImage.tag }}" 29 | imagePullPolicy: "{{ .Values.hyperkubeImage.pullPolicy }}" 30 | command: 31 | - /bin/sh 32 | - -c 33 | - > 34 | kubectl delete crd compositecontrollers.metac.openebs.io; 35 | kubectl delete crd controllerrevisions.metac.openebs.io; 36 | kubectl delete crd decoratorcontrollers.metac.openebs.io; 37 | restartPolicy: OnFailure 38 | {{- end }} 39 | -------------------------------------------------------------------------------- /helm/metac/templates/crds.yaml: -------------------------------------------------------------------------------- 1 | {{ if .Values.crds.create }} 2 | {{- range $path, $bytes := .Files.Glob "crds/**.yaml" }} 3 | {{ $.Files.Get $path }} 4 | --- 5 | {{- end }} 6 | {{- end }} 7 | -------------------------------------------------------------------------------- /helm/metac/templates/role.yaml: -------------------------------------------------------------------------------- 1 | {{ if .Values.rbac.create }} 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: {{ template "metac.fullname" . }} 6 | rules: 7 | - apiGroups: 8 | {{- range .Values.rbac.apiGroups}} 9 | - {{ . | quote }} 10 | {{- end }} 11 | resources: 12 | {{- range .Values.rbac.resources}} 13 | - {{ . | quote }} 14 | {{- end }} 15 | verbs: 16 | {{- range .Values.rbac.verbs}} 17 | - {{ . | quote }} 18 | {{- end }} 19 | {{ end }} 20 | -------------------------------------------------------------------------------- /helm/metac/templates/rolebinding.yaml: -------------------------------------------------------------------------------- 1 | {{ if .Values.rbac.create }} 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRoleBinding 4 | metadata: 5 | name: {{ template "metac.fullname" . }} 6 | subjects: 7 | - kind: ServiceAccount 8 | name: {{ template "metac.serviceAccountName" . }} 9 | namespace: {{ .Release.Namespace }} 10 | roleRef: 11 | kind: ClusterRole 12 | name: {{ template "metac.fullname" . }} 13 | apiGroup: rbac.authorization.k8s.io 14 | {{ end }} 15 | -------------------------------------------------------------------------------- /helm/metac/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | {{ if .Values.serviceAccount.create }} 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: {{ template "metac.serviceAccountName" . }} 6 | namespace: {{ .Release.Namespace }} 7 | {{ end }} 8 | -------------------------------------------------------------------------------- /helm/metac/templates/statefulset.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: StatefulSet 3 | metadata: 4 | name: {{ template "metac.fullname" . }} 5 | namespace: {{ .Release.Namespace }} 6 | labels: 7 | app: {{ template "metac.fullname" . }} 8 | {{- include "metac.labels" . | nindent 4 }} 9 | spec: 10 | replicas: 1 11 | selector: 12 | matchLabels: 13 | app: {{ template "metac.fullname" . }} 14 | serviceName: "" 15 | template: 16 | metadata: 17 | labels: 18 | app: {{ template "metac.fullname" . }} 19 | {{- include "metac.labels" . | nindent 8 }} 20 | spec: 21 | serviceAccountName: {{ template "metac.serviceAccountName" . }} 22 | containers: 23 | - name: {{ template "metac.fullname" . }} 24 | image: {{ .Values.image.repository }}:{{ .Values.image.tag}} 25 | command: ["/usr/bin/metac"] 26 | args: 27 | - --logtostderr 28 | - -v={{ .Values.logLevel }} 29 | - --discovery-interval={{ .Values.discoveryInterval }} 30 | - --cache-flush-interval={{ .Values.cacheFlushInterval }} 31 | - --workers-count={{ .Values.workerCount }} 32 | - --client-go-qps={{ .Values.clientGoQps }} 33 | - --client-go-burst={{ .Values.clientGoBurst }} 34 | resources: 35 | {{- toYaml .Values.resources | nindent 12 }} 36 | volumeClaimTemplates: [] 37 | -------------------------------------------------------------------------------- /helm/metac/values.yaml: -------------------------------------------------------------------------------- 1 | # Default values for metac. 2 | # This is a YAML-formatted file. 3 | # Declare variables to be passed into your templates. 4 | 5 | ## Metac image to use 6 | ## 7 | image: 8 | repository: quay.io/amitkumardas/metac 9 | tag: latest 10 | 11 | discoveryInterval: 10s 12 | cacheFlushInterval: 24h 13 | workerCount: 5 14 | logLevel: 1 15 | clientGoQps: 5 16 | clientGoBurst: 10 17 | 18 | rbac: 19 | create: true 20 | apiGroups: 21 | - "*" 22 | resources: 23 | - "*" 24 | verbs: 25 | - "*" 26 | 27 | ## Service account for Metac to use. 28 | ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/ 29 | ## 30 | serviceAccount: 31 | # Specifies whether a ServiceAccount should be created 32 | create: true 33 | # The name of the ServiceAccount to use. 34 | # If not set and create is true, a name is generated using the fullname template 35 | name: 36 | 37 | ## Should the helm chart manage CRDs? 38 | ## 39 | crds: 40 | # Specifies whether installing the helm chart should install the CRDs 41 | create: true 42 | # Should deleting the helm release also delete the CRDs? 43 | cleanup: true 44 | 45 | ## Hyperkube image to use when cleaning up 46 | ## 47 | hyperkubeImage: 48 | repository: gcr.io/google-containers/hyperkube 49 | tag: v1.12.1 50 | pullPolicy: IfNotPresent 51 | 52 | resources: 53 | limits: 54 | cpu: 80m 55 | memory: 80Mi 56 | requests: 57 | cpu: 50m 58 | memory: 60Mi 59 | -------------------------------------------------------------------------------- /hooks/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 The MayaData Authors. 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 | https://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 hooks will have logic corresponding to specific hook 18 | // implementations. For example, procedure to invoke a webhook 19 | // is coded in this package. Similarly, inline hook implementation 20 | // has its logic in this package. 21 | package hooks 22 | -------------------------------------------------------------------------------- /hooks/hooks.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 Google Inc. 3 | Copyright 2019 The MayaData Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | https://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | package hooks 19 | 20 | import ( 21 | "github.com/pkg/errors" 22 | ) 23 | 24 | // Invoker enables invocation of appropriate hook 25 | type Invoker struct { 26 | // InvokeFn abstracts invocation of hook. Typically specific 27 | // hook implementors will have their call methods set here 28 | InvokeFn func(request, response interface{}) error 29 | } 30 | 31 | // InvokerOption is a typed function that helps in building 32 | // up the Invoker instance 33 | // 34 | // This follows the pattern called "functional options" 35 | type InvokerOption func(*Invoker) error 36 | 37 | // NewInvoker returns a new instance of Invoker. 38 | // This requires at-least one option to be sent from 39 | // its callers. 40 | func NewInvoker(must InvokerOption, others ...InvokerOption) (*Invoker, error) { 41 | 42 | var options = []InvokerOption{must} 43 | options = append(options, others...) 44 | 45 | i := &Invoker{} 46 | for _, o := range options { 47 | o(i) 48 | } 49 | 50 | if i.InvokeFn == nil { 51 | return nil, errors.Errorf("Invoke func can't be nil") 52 | } 53 | return i, nil 54 | } 55 | 56 | // Invoke invokes the hook 57 | func (c *Invoker) Invoke(request, response interface{}) error { 58 | return c.InvokeFn(request, response) 59 | } 60 | -------------------------------------------------------------------------------- /kustomization.yaml: -------------------------------------------------------------------------------- 1 | commonLabels: 2 | metac.openebs.io/name: metacontroller 3 | 4 | resources: 5 | - manifests/metacontroller-rbac.yaml 6 | - manifests/metacontroller.yaml 7 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | Copyright 2019 The MayaData Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | https://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | package main 19 | 20 | import ( 21 | "openebs.io/metac/start" 22 | ) 23 | 24 | func main() { 25 | start.Start() 26 | } 27 | -------------------------------------------------------------------------------- /manifests/dev/args.yaml: -------------------------------------------------------------------------------- 1 | # Override args for development mode. 2 | apiVersion: apps/v1 3 | kind: StatefulSet 4 | metadata: 5 | name: metac 6 | namespace: metac 7 | spec: 8 | template: 9 | spec: 10 | containers: 11 | - name: metac 12 | args: 13 | - --logtostderr 14 | - -v=5 15 | - --discovery-interval=5s 16 | -------------------------------------------------------------------------------- /manifests/dev/image.yaml: -------------------------------------------------------------------------------- 1 | # Override image for development mode (skaffold fills in the tag). 2 | apiVersion: apps/v1 3 | kind: StatefulSet 4 | metadata: 5 | name: metac 6 | namespace: metac 7 | spec: 8 | template: 9 | spec: 10 | containers: 11 | - name: metac 12 | image: quay.io/amitkumardas/metac:latest 13 | -------------------------------------------------------------------------------- /manifests/dev/kustomization.yaml: -------------------------------------------------------------------------------- 1 | bases: 2 | - ../.. 3 | resources: 4 | - ../../manifests/metacontroller-namespace.yaml 5 | patches: 6 | - image.yaml 7 | - args.yaml 8 | -------------------------------------------------------------------------------- /manifests/metacontroller-namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: metac 5 | -------------------------------------------------------------------------------- /manifests/metacontroller-rbac.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: metac 5 | namespace: metac 6 | --- 7 | apiVersion: rbac.authorization.k8s.io/v1 8 | kind: ClusterRole 9 | metadata: 10 | name: metac 11 | rules: 12 | - apiGroups: 13 | - "*" 14 | resources: 15 | - "*" 16 | verbs: 17 | - "*" 18 | --- 19 | apiVersion: rbac.authorization.k8s.io/v1 20 | kind: ClusterRoleBinding 21 | metadata: 22 | name: metac 23 | subjects: 24 | - kind: ServiceAccount 25 | name: metac 26 | namespace: metac 27 | roleRef: 28 | kind: ClusterRole 29 | name: metac 30 | apiGroup: rbac.authorization.k8s.io 31 | --- 32 | kind: ClusterRole 33 | apiVersion: rbac.authorization.k8s.io/v1 34 | metadata: 35 | name: aggregate-metac-view 36 | labels: 37 | rbac.authorization.k8s.io/aggregate-to-admin: "true" 38 | rbac.authorization.k8s.io/aggregate-to-edit: "true" 39 | rbac.authorization.k8s.io/aggregate-to-view: "true" 40 | rules: 41 | - apiGroups: 42 | - metac.openebs.io 43 | resources: 44 | - compositecontrollers 45 | - controllerrevisions 46 | - decoratorcontrollers 47 | - genericcontrollers 48 | verbs: 49 | - get 50 | - list 51 | - watch 52 | --- 53 | kind: ClusterRole 54 | apiVersion: rbac.authorization.k8s.io/v1 55 | metadata: 56 | name: aggregate-metac-edit 57 | labels: 58 | rbac.authorization.k8s.io/aggregate-to-admin: "true" 59 | rbac.authorization.k8s.io/aggregate-to-edit: "true" 60 | rules: 61 | - apiGroups: 62 | - metac.openebs.io 63 | resources: 64 | - controllerrevisions 65 | verbs: 66 | - create 67 | - delete 68 | - deletecollection 69 | - get 70 | - list 71 | - patch 72 | - update 73 | - watch 74 | -------------------------------------------------------------------------------- /release.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "branches": ["master"], 3 | "plugins": [ 4 | '@semantic-release/commit-analyzer', 5 | '@semantic-release/release-notes-generator', 6 | '@semantic-release/github'] 7 | } 8 | -------------------------------------------------------------------------------- /skaffold.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: skaffold/v1beta2 2 | kind: Config 3 | build: 4 | artifacts: 5 | - image: enisoc/metacontroller 6 | docker: 7 | dockerfile: Dockerfile.dev 8 | deploy: 9 | kustomize: 10 | path: manifests/dev 11 | -------------------------------------------------------------------------------- /test/integration/config-mode/suite_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 The MayaData Authors. 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 | https://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 configmode 18 | 19 | import ( 20 | "flag" 21 | "testing" 22 | 23 | "k8s.io/klog" 24 | "openebs.io/metac/test/integration/framework" 25 | ) 26 | 27 | // TestMain will run only once when go test is invoked 28 | // against this package. All the other Test* functions 29 | // will be invoked via m.Run call. 30 | // 31 | // NOTE: 32 | // There can be only one TestMain function in the entire 33 | // package 34 | func TestMain(m *testing.M) { 35 | flag.Parse() 36 | 37 | klogFlags := flag.NewFlagSet("klog", flag.ExitOnError) 38 | klog.InitFlags(klogFlags) 39 | 40 | // Sync the glog and klog flags. 41 | flag.CommandLine.VisitAll(func(f1 *flag.Flag) { 42 | f2 := klogFlags.Lookup(f1.Name) 43 | if f2 != nil { 44 | value := f1.Value.String() 45 | f2.Value.Set(value) 46 | } 47 | }) 48 | 49 | // Pass m.Run function to framework which in turn 50 | // sets up a kubernetes environment & then invokes 51 | // m.Run. 52 | err := framework.StartConfigBasedMetacServer(m.Run) 53 | if err != nil { 54 | // Since this is an error we must to invoke os.Exit(1) 55 | // as per TestMain guidelines 56 | klog.Exitf("%+v", err) 57 | } 58 | 59 | defer klog.Flush() 60 | } 61 | -------------------------------------------------------------------------------- /test/integration/crd-mode/suite_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 The MayaData Authors. 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 | https://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 crdmode 18 | 19 | import ( 20 | "flag" 21 | "testing" 22 | 23 | "k8s.io/klog" 24 | "openebs.io/metac/test/integration/framework" 25 | ) 26 | 27 | // TestMain will run only once when go test is invoked 28 | // against this package. All the other Test* functions 29 | // will be invoked via m.Run call. 30 | // 31 | // NOTE: 32 | // There can be only one TestMain function in the entire 33 | // package 34 | func TestMain(m *testing.M) { 35 | flag.Parse() 36 | 37 | klogFlags := flag.NewFlagSet("klog", flag.ExitOnError) 38 | klog.InitFlags(klogFlags) 39 | 40 | // Sync the glog and klog flags. 41 | flag.CommandLine.VisitAll(func(f1 *flag.Flag) { 42 | f2 := klogFlags.Lookup(f1.Name) 43 | if f2 != nil { 44 | value := f1.Value.String() 45 | f2.Value.Set(value) 46 | } 47 | }) 48 | 49 | // Pass m.Run function to framework which in turn 50 | // sets up a kubernetes environment & then invokes 51 | // m.Run. 52 | err := framework.StartCRDBasedMetacServer(m.Run) 53 | if err != nil { 54 | // Since this is an error we must to invoke os.Exit(1) 55 | // as per TestMain guidelines 56 | klog.Exitf("%+v", err) 57 | } 58 | 59 | defer klog.Flush() 60 | } 61 | -------------------------------------------------------------------------------- /test/integration/framework/README.md: -------------------------------------------------------------------------------- 1 | # Integration Testing Framework 2 | 3 | This package has been taken from [https://github.com/kubernetes-sigs/controller-runtime/tree/master/pkg/internal/testing/integration](https://github.com/kubernetes-sigs/controller-runtime/tree/master/pkg/internal/testing/integration). 4 | 5 | A framework for integration testing components of kubernetes. This framework is intended to work properly both in CI, and on a local dev machine. It therefore explicitly supports both Linux and Darwin. -------------------------------------------------------------------------------- /test/integration/framework/addr/manager.go: -------------------------------------------------------------------------------- 1 | package addr 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | const ( 11 | portReserveTime = 1 * time.Minute 12 | portConflictRetry = 100 13 | ) 14 | 15 | type portCache struct { 16 | lock sync.Mutex 17 | ports map[int]time.Time 18 | } 19 | 20 | func (c *portCache) add(port int) bool { 21 | c.lock.Lock() 22 | defer c.lock.Unlock() 23 | // remove outdated port 24 | for p, t := range c.ports { 25 | if time.Since(t) > portReserveTime { 26 | delete(c.ports, p) 27 | } 28 | } 29 | // try allocating new port 30 | if _, ok := c.ports[port]; ok { 31 | return false 32 | } 33 | c.ports[port] = time.Now() 34 | return true 35 | } 36 | 37 | var cache = &portCache{ 38 | ports: make(map[int]time.Time), 39 | } 40 | 41 | func suggest() (port int, resolvedHost string, err error) { 42 | addr, err := net.ResolveTCPAddr("tcp", "localhost:0") 43 | if err != nil { 44 | return 45 | } 46 | l, err := net.ListenTCP("tcp", addr) 47 | if err != nil { 48 | return 49 | } 50 | port = l.Addr().(*net.TCPAddr).Port 51 | defer func() { 52 | err = l.Close() 53 | }() 54 | resolvedHost = addr.IP.String() 55 | return 56 | } 57 | 58 | // Suggest suggests an address a process can listen on. It returns 59 | // a tuple consisting of a free port and the hostname resolved to its IP. 60 | // It makes sure that new port allocated does not conflict with old ports 61 | // allocated within 1 minute. 62 | func Suggest() (port int, resolvedHost string, err error) { 63 | for i := 0; i < portConflictRetry; i++ { 64 | port, resolvedHost, err = suggest() 65 | if err != nil { 66 | return 67 | } 68 | if cache.add(port) { 69 | return 70 | } 71 | } 72 | err = fmt.Errorf("no free ports found after %d retries", portConflictRetry) 73 | return 74 | } 75 | -------------------------------------------------------------------------------- /test/integration/framework/assets/bin/.gitkeep: -------------------------------------------------------------------------------- 1 | This directory will be the home of some binaries which are downloaded with `make integration-test` 2 | -------------------------------------------------------------------------------- /test/integration/framework/internal/apiserver.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | // APIServerDefaultArgs allow tests to run offline, by preventing API server from attempting to 4 | // use default route to determine its --advertise-address. 5 | var APIServerDefaultArgs = []string{ 6 | "--advertise-address=127.0.0.1", 7 | "--etcd-servers={{ if .EtcdURL }}{{ .EtcdURL.String }}{{ end }}", 8 | "--cert-dir={{ .CertDir }}", 9 | "--insecure-port={{ if .URL }}{{ .URL.Port }}{{ end }}", 10 | "--insecure-bind-address={{ if .URL }}{{ .URL.Hostname }}{{ end }}", 11 | "--secure-port={{ if .SecurePort }}{{ .SecurePort }}{{ end }}", 12 | "--disable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,TaintNodesByCondition,Priority,DefaultTolerationSeconds,DefaultStorageClass,StorageObjectInUseProtection,PersistentVolumeClaimResize,ResourceQuota", //nolint 13 | "--service-cluster-ip-range=10.0.0.0/24", 14 | "--allow-privileged=true", 15 | } 16 | 17 | // DoAPIServerArgDefaulting will set default values to allow tests to run offline when the args are not informed. Otherwise, 18 | // it will return the same []string arg passed as param. 19 | func DoAPIServerArgDefaulting(args []string) []string { 20 | if len(args) != 0 { 21 | return args 22 | } 23 | 24 | return APIServerDefaultArgs 25 | } 26 | -------------------------------------------------------------------------------- /test/integration/framework/internal/arguments.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "bytes" 5 | "html/template" 6 | ) 7 | 8 | // RenderTemplates returns an []string to render the templates 9 | func RenderTemplates(templates []string, data interface{}) (args []string, err error) { 10 | var t *template.Template 11 | 12 | for _, tpl := range templates { 13 | t, err = template.New(tpl).Parse(tpl) 14 | if err != nil { 15 | args = nil 16 | return 17 | } 18 | 19 | buf := &bytes.Buffer{} 20 | err = t.Execute(buf, data) 21 | if err != nil { 22 | args = nil 23 | return 24 | } 25 | args = append(args, buf.String()) 26 | } 27 | 28 | return 29 | } 30 | -------------------------------------------------------------------------------- /test/integration/framework/internal/bin_path_finder.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | "regexp" 7 | "runtime" 8 | "strings" 9 | ) 10 | 11 | var assetsPath string 12 | 13 | func init() { 14 | _, thisFile, _, ok := runtime.Caller(0) 15 | if !ok { 16 | panic("Could not determine the path of the BinPathFinder") 17 | } 18 | assetsPath = filepath.Join( 19 | filepath.Dir(thisFile), 20 | "..", 21 | "assets", 22 | "bin", 23 | ) 24 | } 25 | 26 | // BinPathFinder checks the environment variable, 27 | // derived from the symbolic name, and falls back 28 | // to a default assets location when this variable is not set 29 | func BinPathFinder(symbolicName string) (binPath string) { 30 | punctuationPattern := regexp.MustCompile("[^A-Z0-9]+") 31 | sanitizedName := punctuationPattern.ReplaceAllString(strings.ToUpper(symbolicName), "_") 32 | leadingNumberPattern := regexp.MustCompile("^[0-9]+") 33 | sanitizedName = leadingNumberPattern.ReplaceAllString(sanitizedName, "") 34 | envVar := "TEST_ASSET_" + sanitizedName 35 | 36 | if val, ok := os.LookupEnv(envVar); ok { 37 | return val 38 | } 39 | 40 | return filepath.Join(assetsPath, symbolicName) 41 | } 42 | -------------------------------------------------------------------------------- /test/integration/framework/internal/etcd.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "net/url" 5 | ) 6 | 7 | // EtcdDefaultArgs allow tests to run offline, by preventing 8 | // API server from attempting to use default route to determine 9 | // its urls. 10 | var EtcdDefaultArgs = []string{ 11 | "--listen-peer-urls=http://localhost:0", 12 | "--advertise-client-urls={{ if .URL }}{{ .URL.String }}{{ end }}", 13 | "--listen-client-urls={{ if .URL }}{{ .URL.String }}{{ end }}", 14 | "--data-dir={{ .DataDir }}", 15 | } 16 | 17 | // DoEtcdArgDefaulting will set default values to allow tests 18 | // to run offline when the args are not informed. Otherwise, 19 | // it will return the same []string arg passed as param. 20 | func DoEtcdArgDefaulting(args []string) []string { 21 | if len(args) != 0 { 22 | return args 23 | } 24 | 25 | return EtcdDefaultArgs 26 | } 27 | 28 | // isSecureScheme returns false when the schema is insecure. 29 | func isSecureScheme(scheme string) bool { 30 | // https://github.com/coreos/etcd/blob/d9deeff49a080a88c982d328ad9d33f26d1ad7b6/pkg/transport/listener.go#L53 31 | if scheme == "https" || scheme == "unixs" { 32 | return true 33 | } 34 | return false 35 | } 36 | 37 | // GetEtcdStartMessage returns the expected message when etcd 38 | // starts up. This message is based on the URL scheme 39 | func GetEtcdStartMessage(listenURL url.URL) string { 40 | if isSecureScheme(listenURL.Scheme) { 41 | // https://github.com/coreos/etcd/blob/a7f1fbe00ec216fcb3a1919397a103b41dca8413/embed/serve.go#L167 42 | return "serving client requests on " 43 | } 44 | 45 | // https://github.com/coreos/etcd/blob/a7f1fbe00ec216fcb3a1919397a103b41dca8413/embed/serve.go#L124 46 | return "serving insecure client requests on " 47 | } 48 | -------------------------------------------------------------------------------- /test/integration/framework/webhook.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Google Inc. 3 | Copyright 2019 The MayaData Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | https://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | package framework 19 | 20 | import ( 21 | "fmt" 22 | "io/ioutil" 23 | "net/http" 24 | "net/http/httptest" 25 | ) 26 | 27 | // ServeWebhook is a helper for quickly creating a 28 | // webhook server in tests. 29 | func (f *Fixture) ServeWebhook( 30 | reconciler func(request []byte) (response []byte, err error), 31 | ) *httptest.Server { 32 | // create a new instance of http test server 33 | srv := httptest.NewServer( 34 | http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 35 | if r.Method != http.MethodPost { 36 | http.Error( 37 | w, 38 | fmt.Sprintf("Unsupported method: %s", r.Method), 39 | http.StatusMethodNotAllowed, 40 | ) 41 | return 42 | } 43 | 44 | body, err := ioutil.ReadAll(r.Body) 45 | r.Body.Close() 46 | if err != nil { 47 | http.Error( 48 | w, 49 | fmt.Sprintf("Can't read body: %v", err), 50 | http.StatusBadRequest, 51 | ) 52 | return 53 | } 54 | // invoke reconciler here 55 | resp, err := reconciler(body) 56 | if err != nil { 57 | http.Error(w, err.Error(), http.StatusInternalServerError) 58 | return 59 | } 60 | w.Header().Set("Content-Type", "application/json") 61 | w.Write(resp) 62 | }), 63 | ) 64 | f.addToTeardown(func() error { 65 | srv.Close() 66 | return nil 67 | }) 68 | return srv 69 | } 70 | -------------------------------------------------------------------------------- /third_party/kubernetes/pointer.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 The Kubernetes Authors. 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 kubernetes 18 | 19 | // This is copied from k8s.io/kubernetes to avoid a dependency on all of Kubernetes. 20 | // TODO(enisoc): Move the upstream code to somewhere better. 21 | 22 | // BoolPtr returns a pointer to the given bool 23 | func BoolPtr(b bool) *bool { 24 | o := b 25 | return &o 26 | } 27 | 28 | // Int32Ptr returns a pointer to the given int32 29 | func Int32Ptr(i int32) *int32 { 30 | o := i 31 | return &o 32 | } 33 | 34 | // IntPtr returns a pointer to the given int 35 | func IntPtr(i int) *int { 36 | o := i 37 | return &o 38 | } 39 | 40 | // StringPtr returns a pointer to the given string 41 | func StringPtr(s string) *string { 42 | o := s 43 | return &o 44 | } 45 | --------------------------------------------------------------------------------