├── .circleci └── config.yml ├── .dockerignore ├── .gitignore ├── Dockerfile ├── Gopkg.lock ├── Gopkg.toml ├── LICENSE ├── Makefile ├── README.md ├── docs ├── _config.yml ├── controller.yaml ├── deploy.yaml ├── index.md ├── runner.sh ├── test-run.yaml └── tests.yaml ├── hack ├── boilerplate.go.txt └── update-client-gen.sh ├── main.go └── pkg ├── apis └── tester │ ├── doc.go │ ├── install │ └── install.go │ ├── register.go │ ├── types.go │ ├── v1alpha1 │ ├── conversion.go │ ├── defaults.go │ ├── doc.go │ ├── register.go │ ├── types.go │ ├── zz_generated.conversion.go │ ├── zz_generated.deepcopy.go │ └── zz_generated.defaults.go │ ├── zz_generated.deepcopy.go │ └── zz_generated.defaults.go ├── client ├── clientset.go ├── doc.go ├── fake │ ├── clientset_generated.go │ ├── doc.go │ └── register.go ├── internalclientset │ ├── clientset.go │ ├── doc.go │ ├── fake │ │ ├── clientset_generated.go │ │ ├── doc.go │ │ └── register.go │ ├── scheme │ │ ├── doc.go │ │ └── register.go │ └── typed │ │ └── tester │ │ └── internalversion │ │ ├── doc.go │ │ ├── fake │ │ ├── doc.go │ │ └── fake_tester_client.go │ │ ├── generated_expansion.go │ │ └── tester_client.go ├── scheme │ ├── doc.go │ └── register.go └── typed │ └── srossross │ └── v1alpha1 │ ├── doc.go │ ├── fake │ ├── doc.go │ ├── fake_srossross_client.go │ ├── fake_testrun.go │ └── fake_testtemplate.go │ ├── generated_expansion.go │ ├── srossross_client.go │ ├── testrun.go │ └── testtemplate.go ├── controller ├── controller.go ├── controller_test.go ├── fake │ └── fake.go ├── filters.go ├── filters_test.go ├── informer_utils.go ├── keys.go ├── keys_test.go └── remove_pods.go ├── informers ├── externalversions │ ├── factory.go │ ├── generic.go │ ├── internalinterfaces │ │ └── factory_interfaces.go │ └── srossross │ │ ├── interface.go │ │ └── v1alpha1 │ │ ├── interface.go │ │ ├── testrun.go │ │ └── testtemplate.go └── internalversion │ ├── factory.go │ ├── generic.go │ └── internalinterfaces │ └── factory_interfaces.go ├── listers └── srossross │ └── v1alpha1 │ ├── expansion_generated.go │ ├── testrun.go │ └── testtemplate.go ├── loop ├── work.go └── work_test.go └── run ├── TestRunner.go ├── crd.go ├── events.go ├── fake └── fake.go ├── informers.go ├── podutils.go ├── reconcile.go └── runner.go /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # Golang CircleCI 2.0 configuration file 2 | # 3 | # Check https://circleci.com/docs/2.0/language-go/ for more details 4 | version: 2 5 | jobs: 6 | build: 7 | docker: 8 | # specify the version 9 | - image: circleci/golang:1.8 10 | working_directory: /go/src/github.com/srossross/k8s-test-controller 11 | steps: 12 | - checkout 13 | 14 | - restore_cache: 15 | keys: 16 | - Gopkg-{{ checksum "Gopkg.toml" }} 17 | 18 | - run: 19 | name: Dep Ensure 20 | command: | 21 | go get -u github.com/golang/dep/cmd/dep 22 | dep ensure 23 | 24 | - save_cache: 25 | key: Gopkg-{{ checksum "Gopkg.toml" }} 26 | paths: 27 | - vendor 28 | 29 | - run: 30 | name: Go Test 31 | command: go test -v ./pkg/controller/... 32 | - run: mkdir -p /tmp/commands 33 | # - run: $GOPATH/bin/golint ./... 34 | - run: 35 | name: Build Executables 36 | command: | 37 | make build GOOS=linux GOARCH=amd64 38 | - store_artifacts: 39 | path: /tmp/commands 40 | - persist_to_workspace: 41 | root: /tmp/commands 42 | paths: 43 | - k8s-test-controller-linux-amd64.tgz 44 | 45 | deploy: 46 | docker: 47 | # specify the version 48 | - image: circleci/golang:1.8 49 | working_directory: /go/src/github.com/srossross/k8s-test-controller 50 | steps: 51 | - checkout 52 | 53 | - attach_workspace: 54 | at: /tmp/commands 55 | 56 | - run: 57 | name: Create Github Release 58 | command: | 59 | go get github.com/aktau/github-release 60 | 61 | echo Uploading to Release ${CIRCLE_TAG} 62 | echo Uploading to ${CIRCLE_PROJECT_USERNAME} ${CIRCLE_PROJECT_REPONAME} 63 | 64 | make release TAG=${CIRCLE_TAG} 65 | make upload TAG=${CIRCLE_TAG} 66 | 67 | 68 | workflows: 69 | version: 2 70 | build_and_deploy: 71 | jobs: 72 | - build: 73 | filters: 74 | branches: 75 | only: /.*/ 76 | tags: 77 | only: /.*/ 78 | - deploy: 79 | requires: 80 | - build 81 | filters: 82 | branches: 83 | ignore: /.*/ 84 | tags: 85 | only: /v.*/ 86 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | docs/ 2 | hack/ 3 | bin/ 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | .get_deps 3 | .generate_exes 4 | k8s-test-controller 5 | k8s-test-controller-* 6 | vendor/ 7 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.9-alpine as builder 2 | RUN apk update && apk add git 3 | 4 | ENV CGO_ENABLED 0 5 | ENV LDFLAGS "" 6 | WORKDIR "$GOPATH/src/github.com/srossross/k8s-test-controller" 7 | 8 | COPY *.go ./ 9 | COPY pkg ./pkg 10 | COPY ./vendor ./vendor 11 | COPY ./Gopkg.lock ./ 12 | COPY ./Gopkg.toml ./ 13 | 14 | RUN go get -u github.com/golang/dep/cmd/dep 15 | RUN dep ensure 16 | 17 | RUN go build -ldflags "${LDFLAGS}" -o /test-controller ./main.go 18 | 19 | # ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- 20 | # ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- 21 | 22 | FROM alpine 23 | 24 | COPY --from=builder /test-controller /test-controller 25 | CMD ["/test-controller"] 26 | -------------------------------------------------------------------------------- /Gopkg.lock: -------------------------------------------------------------------------------- 1 | # This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. 2 | 3 | 4 | [[projects]] 5 | name = "github.com/PuerkitoBio/purell" 6 | packages = ["."] 7 | revision = "0bcb03f4b4d0a9428594752bd2a3b9aa0a9d4bd4" 8 | version = "v1.1.0" 9 | 10 | [[projects]] 11 | branch = "master" 12 | name = "github.com/PuerkitoBio/urlesc" 13 | packages = ["."] 14 | revision = "de5bf2ad457846296e2031421a34e2568e304e35" 15 | 16 | [[projects]] 17 | name = "github.com/davecgh/go-spew" 18 | packages = ["spew"] 19 | revision = "346938d642f2ec3594ed81d874461961cd0faa76" 20 | version = "v1.1.0" 21 | 22 | [[projects]] 23 | name = "github.com/emicklei/go-restful" 24 | packages = [".","log"] 25 | revision = "5741799b275a3c4a5a9623a993576d7545cf7b5c" 26 | version = "v2.4.0" 27 | 28 | [[projects]] 29 | name = "github.com/emicklei/go-restful-swagger12" 30 | packages = ["."] 31 | revision = "dcef7f55730566d41eae5db10e7d6981829720f6" 32 | version = "1.0.1" 33 | 34 | [[projects]] 35 | name = "github.com/ghodss/yaml" 36 | packages = ["."] 37 | revision = "0ca9ea5df5451ffdf184b4428c902747c2c11cd7" 38 | version = "v1.0.0" 39 | 40 | [[projects]] 41 | branch = "master" 42 | name = "github.com/go-openapi/jsonpointer" 43 | packages = ["."] 44 | revision = "779f45308c19820f1a69e9a4cd965f496e0da10f" 45 | 46 | [[projects]] 47 | branch = "master" 48 | name = "github.com/go-openapi/jsonreference" 49 | packages = ["."] 50 | revision = "36d33bfe519efae5632669801b180bf1a245da3b" 51 | 52 | [[projects]] 53 | branch = "master" 54 | name = "github.com/go-openapi/spec" 55 | packages = ["."] 56 | revision = "84b5bee7bcb76f3d17bcbaf421bac44bd5709ca6" 57 | 58 | [[projects]] 59 | branch = "master" 60 | name = "github.com/go-openapi/swag" 61 | packages = ["."] 62 | revision = "f3f9494671f93fcff853e3c6e9e948b3eb71e590" 63 | 64 | [[projects]] 65 | name = "github.com/gogo/protobuf" 66 | packages = ["proto","sortkeys"] 67 | revision = "342cbe0a04158f6dcb03ca0079991a51a4248c02" 68 | version = "v0.5" 69 | 70 | [[projects]] 71 | branch = "master" 72 | name = "github.com/golang/glog" 73 | packages = ["."] 74 | revision = "23def4e6c14b4da8ac2ed8007337bc5eb5007998" 75 | 76 | [[projects]] 77 | branch = "master" 78 | name = "github.com/golang/protobuf" 79 | packages = ["proto","ptypes","ptypes/any","ptypes/duration","ptypes/timestamp"] 80 | revision = "1643683e1b54a9e88ad26d98f81400c8c9d9f4f9" 81 | 82 | [[projects]] 83 | branch = "master" 84 | name = "github.com/google/btree" 85 | packages = ["."] 86 | revision = "316fb6d3f031ae8f4d457c6c5186b9e3ded70435" 87 | 88 | [[projects]] 89 | branch = "master" 90 | name = "github.com/google/gofuzz" 91 | packages = ["."] 92 | revision = "24818f796faf91cd76ec7bddd72458fbced7a6c1" 93 | 94 | [[projects]] 95 | name = "github.com/googleapis/gnostic" 96 | packages = ["OpenAPIv2","compiler","extensions"] 97 | revision = "ee43cbb60db7bd22502942cccbc39059117352ab" 98 | version = "v0.1.0" 99 | 100 | [[projects]] 101 | branch = "master" 102 | name = "github.com/gregjones/httpcache" 103 | packages = [".","diskcache"] 104 | revision = "c1f8028e62adb3d518b823a2f8e6a95c38bdd3aa" 105 | 106 | [[projects]] 107 | branch = "master" 108 | name = "github.com/hashicorp/golang-lru" 109 | packages = [".","simplelru"] 110 | revision = "0a025b7e63adc15a622f29b0b2c4c3848243bbf6" 111 | 112 | [[projects]] 113 | branch = "master" 114 | name = "github.com/howeyc/gopass" 115 | packages = ["."] 116 | revision = "bf9dde6d0d2c004a008c27aaee91170c786f6db8" 117 | 118 | [[projects]] 119 | name = "github.com/imdario/mergo" 120 | packages = ["."] 121 | revision = "7fe0c75c13abdee74b09fcacef5ea1c6bba6a874" 122 | version = "0.2.4" 123 | 124 | [[projects]] 125 | name = "github.com/json-iterator/go" 126 | packages = ["."] 127 | revision = "6240e1e7983a85228f7fd9c3e1b6932d46ec58e2" 128 | version = "1.0.3" 129 | 130 | [[projects]] 131 | branch = "master" 132 | name = "github.com/juju/ratelimit" 133 | packages = ["."] 134 | revision = "5b9ff866471762aa2ab2dced63c9fb6f53921342" 135 | 136 | [[projects]] 137 | branch = "master" 138 | name = "github.com/mailru/easyjson" 139 | packages = ["buffer","jlexer","jwriter"] 140 | revision = "efb36c3268025336c3cdd805e3be5f88d1f62b73" 141 | 142 | [[projects]] 143 | branch = "master" 144 | name = "github.com/petar/GoLLRB" 145 | packages = ["llrb"] 146 | revision = "53be0d36a84c2a886ca057d34b6aa4468df9ccb4" 147 | 148 | [[projects]] 149 | name = "github.com/peterbourgon/diskv" 150 | packages = ["."] 151 | revision = "5f041e8faa004a95c88a202771f4cc3e991971e6" 152 | version = "v2.0.1" 153 | 154 | [[projects]] 155 | name = "github.com/pmezard/go-difflib" 156 | packages = ["difflib"] 157 | revision = "792786c7400a136282c1664665ae0a8db921c6c2" 158 | version = "v1.0.0" 159 | 160 | [[projects]] 161 | name = "github.com/spf13/pflag" 162 | packages = ["."] 163 | revision = "e57e3eeb33f795204c1ca35f56c44f83227c6e66" 164 | version = "v1.0.0" 165 | 166 | [[projects]] 167 | name = "github.com/stretchr/testify" 168 | packages = ["assert"] 169 | revision = "69483b4bd14f5845b5a1e55bca19e954e827f1d0" 170 | version = "v1.1.4" 171 | 172 | [[projects]] 173 | branch = "master" 174 | name = "golang.org/x/crypto" 175 | packages = ["ssh/terminal"] 176 | revision = "edd5e9b0879d13ee6970a50153d85b8fec9f7686" 177 | 178 | [[projects]] 179 | branch = "master" 180 | name = "golang.org/x/net" 181 | packages = ["context","http2","http2/hpack","idna","lex/httplex"] 182 | revision = "cd69bc3fc700721b709c3a59e16e24c67b58f6ff" 183 | 184 | [[projects]] 185 | branch = "master" 186 | name = "golang.org/x/sys" 187 | packages = ["unix","windows"] 188 | revision = "8dbc5d05d6edcc104950cc299a1ce6641235bc86" 189 | 190 | [[projects]] 191 | branch = "master" 192 | name = "golang.org/x/text" 193 | packages = ["collate","collate/build","internal/colltab","internal/gen","internal/tag","internal/triegen","internal/ucd","language","secure/bidirule","transform","unicode/bidi","unicode/cldr","unicode/norm","unicode/rangetable","width"] 194 | revision = "c01e4764d870b77f8abe5096ee19ad20d80e8075" 195 | 196 | [[projects]] 197 | name = "gopkg.in/inf.v0" 198 | packages = ["."] 199 | revision = "3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4" 200 | version = "v0.9.0" 201 | 202 | [[projects]] 203 | branch = "v2" 204 | name = "gopkg.in/yaml.v2" 205 | packages = ["."] 206 | revision = "eb3733d160e74a9c7e442f435eb3bea458e1d19f" 207 | 208 | [[projects]] 209 | branch = "release-1.8" 210 | name = "k8s.io/api" 211 | packages = ["admissionregistration/v1alpha1","apps/v1beta1","apps/v1beta2","authentication/v1","authentication/v1beta1","authorization/v1","authorization/v1beta1","autoscaling/v1","autoscaling/v2beta1","batch/v1","batch/v1beta1","batch/v2alpha1","certificates/v1beta1","core/v1","extensions/v1beta1","networking/v1","policy/v1beta1","rbac/v1","rbac/v1alpha1","rbac/v1beta1","scheduling/v1alpha1","settings/v1alpha1","storage/v1","storage/v1beta1"] 212 | revision = "6c6dac0277229b9e9578c5ca3f74a4345d35cdc2" 213 | 214 | [[projects]] 215 | branch = "release-1.8" 216 | name = "k8s.io/apiextensions-apiserver" 217 | packages = ["pkg/apis/apiextensions","pkg/apis/apiextensions/v1beta1","pkg/client/clientset/clientset","pkg/client/clientset/clientset/scheme","pkg/client/clientset/clientset/typed/apiextensions/v1beta1"] 218 | revision = "89b2a556a65053a90552eaed26f810df4a266367" 219 | 220 | [[projects]] 221 | branch = "release-1.8" 222 | name = "k8s.io/apimachinery" 223 | packages = ["pkg/api/equality","pkg/api/errors","pkg/api/meta","pkg/api/resource","pkg/apimachinery","pkg/apimachinery/announced","pkg/apimachinery/registered","pkg/apis/meta/internalversion","pkg/apis/meta/v1","pkg/apis/meta/v1/unstructured","pkg/apis/meta/v1alpha1","pkg/conversion","pkg/conversion/queryparams","pkg/conversion/unstructured","pkg/fields","pkg/labels","pkg/runtime","pkg/runtime/schema","pkg/runtime/serializer","pkg/runtime/serializer/json","pkg/runtime/serializer/protobuf","pkg/runtime/serializer/recognizer","pkg/runtime/serializer/streaming","pkg/runtime/serializer/versioning","pkg/selection","pkg/types","pkg/util/cache","pkg/util/clock","pkg/util/diff","pkg/util/errors","pkg/util/framer","pkg/util/intstr","pkg/util/json","pkg/util/net","pkg/util/runtime","pkg/util/sets","pkg/util/validation","pkg/util/validation/field","pkg/util/wait","pkg/util/yaml","pkg/version","pkg/watch","third_party/forked/golang/reflect"] 224 | revision = "019ae5ada31de202164b118aee88ee2d14075c31" 225 | 226 | [[projects]] 227 | name = "k8s.io/client-go" 228 | packages = ["discovery","discovery/fake","kubernetes/scheme","kubernetes/typed/core/v1","kubernetes/typed/core/v1/fake","listers/core/v1","pkg/version","rest","rest/watch","testing","tools/auth","tools/cache","tools/clientcmd","tools/clientcmd/api","tools/clientcmd/api/latest","tools/clientcmd/api/v1","tools/metrics","tools/pager","tools/reference","transport","util/cert","util/flowcontrol","util/homedir","util/integer","util/workqueue"] 229 | revision = "2ae454230481a7cb5544325e12ad7658ecccd19b" 230 | version = "kubernetes-1.8.1" 231 | 232 | [[projects]] 233 | branch = "master" 234 | name = "k8s.io/kube-openapi" 235 | packages = ["pkg/common"] 236 | revision = "89ae48fe8691077463af5b7fb3b6f194632c5946" 237 | 238 | [solve-meta] 239 | analyzer-name = "dep" 240 | analyzer-version = 1 241 | inputs-digest = "10ba5a4188519b992e4b803ae2072e06856b89262f78d46391534a50ba72236a" 242 | solver-name = "gps-cdcl" 243 | solver-version = 1 244 | -------------------------------------------------------------------------------- /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 | 24 | [[constraint]] 25 | branch = "master" 26 | name = "github.com/golang/glog" 27 | 28 | [[constraint]] 29 | version = "kubernetes-1.8.1" 30 | name = "k8s.io/api" 31 | 32 | [[constraint]] 33 | version = "kubernetes-1.8.1" 34 | name = "k8s.io/apiextensions-apiserver" 35 | 36 | [[constraint]] 37 | version = "kubernetes-1.8.1" 38 | name = "k8s.io/apimachinery" 39 | 40 | [[constraint]] 41 | version = "kubernetes-1.8.1" 42 | name = "k8s.io/client-go" 43 | 44 | [[constraint]] 45 | name = "github.com/stretchr/testify" 46 | version = "1.1.4" 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Sean Ross-Ross, James Munnelly 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PACKAGE_NAME := github.com/srossross/k8s-test-controller 2 | 3 | # A temporary directory to store generator executors in 4 | BINDIR ?= bin 5 | GOPATH ?= $HOME/go 6 | HACK_DIR ?= hack 7 | 8 | GOOS := $(shell go env GOHOSTOS) 9 | GOARCH := $(shell go env GOHOSTARCH) 10 | CGO_ENABLED := 0 11 | LDFLAGS := -X github.com/srossross/k8s-test-controller/main.VERSION=$(shell echo $${CIRCLE_TAG:-?}) \ 12 | -X github.com/srossross/k8s-test-controller/main.BUILD_TIME=$(shell date -u +%Y-%m-%d) 13 | 14 | USERNAME := $(shell echo ${CIRCLE_PROJECT_USERNAME}) 15 | REPONAME := $(shell echo ${CIRCLE_PROJECT_REPONAME}) 16 | 17 | # A list of all types.go files in pkg/apis 18 | TYPES_FILES = $(shell find pkg/apis -name types.go) 19 | 20 | # This step pulls the Kubernetes repo so we can build generators in another 21 | # target. Soon, github.com/kubernetes/kube-gen will be live meaning we don't 22 | # need to pull the entirety of the k8s source code. 23 | .get_deps: 24 | @echo "Grabbing dependencies..." 25 | @go get -d -u k8s.io/kubernetes/ || true 26 | @go get -d github.com/kubernetes/repo-infra || true 27 | # Once k8s.io/kube-gen is live, we should be able to remove this dependency 28 | # on k8s.io/kubernetes. https://github.com/kubernetes/kubernetes/pull/49114 29 | cd ${GOPATH}/src/k8s.io/kubernetes; git checkout 25d3523359ff17dda6deb867a7c3dd6c8b7ea705; 30 | @touch $@ 31 | 32 | # Targets for building k8s code generators 33 | ################################################# 34 | .generate_exes: .get_deps \ 35 | $(BINDIR)/defaulter-gen \ 36 | $(BINDIR)/deepcopy-gen \ 37 | $(BINDIR)/conversion-gen \ 38 | $(BINDIR)/client-gen \ 39 | $(BINDIR)/lister-gen \ 40 | $(BINDIR)/informer-gen 41 | touch $@ 42 | 43 | $(BINDIR)/defaulter-gen: 44 | go build -o $@ k8s.io/kubernetes/cmd/libs/go2idl/defaulter-gen 45 | 46 | $(BINDIR)/deepcopy-gen: 47 | go build -o $@ k8s.io/kubernetes/cmd/libs/go2idl/deepcopy-gen 48 | 49 | $(BINDIR)/conversion-gen: 50 | go build -o $@ k8s.io/kubernetes/cmd/libs/go2idl/conversion-gen 51 | 52 | $(BINDIR)/client-gen: 53 | go build -o $@ k8s.io/kubernetes/cmd/libs/go2idl/client-gen 54 | 55 | $(BINDIR)/lister-gen: 56 | go build -o $@ k8s.io/kubernetes/cmd/libs/go2idl/lister-gen 57 | 58 | $(BINDIR)/informer-gen: 59 | go build -o $@ k8s.io/kubernetes/cmd/libs/go2idl/informer-gen 60 | ################################################# 61 | 62 | 63 | # This target runs all required generators against our API types. 64 | generate: .generate_exes $(TYPES_FILES) ## Generate files 65 | # Generate defaults 66 | $(BINDIR)/defaulter-gen \ 67 | --v 1 --logtostderr \ 68 | --go-header-file "$${GOPATH}/src/github.com/srossross/k8s-test-controller/hack/boilerplate.go.txt" \ 69 | --input-dirs "$(PACKAGE_NAME)/pkg/apis/tester" \ 70 | --input-dirs "$(PACKAGE_NAME)/pkg/apis/tester/v1alpha1" \ 71 | --extra-peer-dirs "$(PACKAGE_NAME)/pkg/apis/tester" \ 72 | --extra-peer-dirs "$(PACKAGE_NAME)/pkg/apis/tester/v1alpha1" \ 73 | --output-file-base "zz_generated.defaults" 74 | # Generate deep copies 75 | $(BINDIR)/deepcopy-gen \ 76 | --v 1 --logtostderr \ 77 | --go-header-file "$${GOPATH}/src/github.com/srossross/k8s-test-controller/hack/boilerplate.go.txt" \ 78 | --input-dirs "$(PACKAGE_NAME)/pkg/apis/tester" \ 79 | --input-dirs "$(PACKAGE_NAME)/pkg/apis/tester/v1alpha1" \ 80 | --output-file-base zz_generated.deepcopy 81 | # Generate conversions 82 | $(BINDIR)/conversion-gen \ 83 | --v 1 --logtostderr \ 84 | --go-header-file "$${GOPATH}/src/github.com/srossross/k8s-test-controller/hack/boilerplate.go.txt" \ 85 | --input-dirs "$(PACKAGE_NAME)/pkg/apis/tester" \ 86 | --input-dirs "$(PACKAGE_NAME)/pkg/apis/tester/v1alpha1" \ 87 | --output-file-base zz_generated.conversion 88 | # generate all pkg/client contents 89 | $(HACK_DIR)/update-client-gen.sh 90 | 91 | cacheBuilds: ## Make go build and go run faster 92 | go list -f '{{.Deps}}' ./... | tr "[" " " | tr "]" " " | xargs go list -f '{{if not .Standard}}{{.ImportPath}}{{end}}' | xargs go install -a 93 | 94 | 95 | build: ## build for any arch 96 | mkdir -p /tmp/commands 97 | 98 | CGO_ENABLED=$(CGO_ENABLED) GOOS=$(GOOS) GOARCH=$(GOARCH) go build -ldflags "$(LDFLAGS)" -o ./k8s-test-controller-$(GOOS)-$(GOARCH) ./main.go 99 | tar -zcvf /tmp/commands/k8s-test-controller-$(GOOS)-$(GOARCH).tgz ./k8s-test-controller-$(GOOS)-$(GOARCH) 100 | 101 | buildLinux: GOOS := linux 102 | buildLinux: GOARCH := amd64 103 | buildLinux: build 104 | 105 | dockerBuild: ## Build docker container 106 | docker build -t srossross/k8s-test-controller:latest . 107 | 108 | release: ## Create github release 109 | github-release release \ 110 | --user $(USERNAME) \ 111 | --repo $(REPONAME) \ 112 | --tag $(TAG) \ 113 | --name "Release $(TAG)" \ 114 | --description "TODO: Description" 115 | 116 | upload: ## Upload build artifacts to github 117 | 118 | github-release upload \ 119 | --user $(USERNAME) \ 120 | --repo $(REPONAME) \ 121 | --tag $(TAG) \ 122 | --name "k8s-test-controller-linux-amd64." \ 123 | --file /tmp/commands/k8s-test-controller-linux-amd64.tgz 124 | 125 | 126 | 127 | .PHONY: help 128 | 129 | help: ## show this help and exit 130 | @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' 131 | 132 | .DEFAULT_GOAL := help 133 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # k8s-test-controller 2 | 3 | Add test resources to test your Kubernetes cluster setup. 4 | 5 | [Check out the docs](https://srossross.github.io/k8s-test-controller/) 6 | --- 7 | 8 | # Development 9 | 10 | ## Generating code 11 | 12 | If you change any API types, you must regenerate all of the supporting code. 13 | This can be done by using the `generate` make target, e.g. 14 | 15 | ```bash 16 | $ make generate 17 | ``` 18 | 19 | The generators tend not to **error** even if something you may consider to be a 20 | problem occurs, thus it's important to ensure you can still build your 21 | application after running the generators. 22 | 23 | ## Building 24 | 25 | The actual golang app can be built with a simple `go build`. The generated 26 | files required are committed to the repo to ensure it stays in sync. We should 27 | also use a `verify` step to ensure that the generated files are in sync with 28 | their respective types.go files, but for brevity have omitted that here. 29 | 30 | ```bash 31 | $ go build 32 | ``` 33 | 34 | ## Running 35 | 36 | Running the application is as follows: 37 | 38 | ```bash 39 | $ go run main.go -kubeconfig ~/.kube/config 40 | ``` 41 | 42 | Then we can go ahead and create a Tests and TestRuns! 43 | 44 | ```bash 45 | $ kubectl create -f docs/tests.yaml 46 | $ kubectl create -f docs/test-run.yaml 47 | ``` 48 | -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /docs/controller.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1beta1 2 | kind: Deployment 3 | metadata: 4 | name: test-controller-deployment 5 | labels: 6 | app: test-controller 7 | namespace: kube-system 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app: test-controller 13 | template: 14 | metadata: 15 | labels: 16 | app: test-controller 17 | spec: 18 | containers: 19 | - name: test-controller 20 | image: srossross/k8s-test-controller:latest 21 | -------------------------------------------------------------------------------- /docs/deploy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1beta1 2 | kind: Deployment 3 | metadata: 4 | name: test-controller-deployment 5 | labels: 6 | app: test-controller 7 | namespace: kube-system 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app: test-controller 13 | template: 14 | metadata: 15 | labels: 16 | app: test-controller 17 | spec: 18 | containers: 19 | - name: test-controller 20 | image: srossross/k8s-test-controller:latest 21 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | 2 | # Kubernetes Test Controller 3 | 4 | - Run end to end tests on your k8s application 5 | - Run targeted component tests after a rollout or change 6 | 7 | ## Motivation 8 | 9 | ### Why Test? 10 | 11 | - Rollout your CI/CD pipeline with confidence 12 | - Validate that your configuration is correct 13 | - Make sure your username and password work correctly 14 | - Make sure an incorrect username and password does not work 15 | - Assert that your services are up and correctly load balancing 16 | 17 | ## Cluster Installation 18 | 19 | To use this test controller run: 20 | 21 | ```sh 22 | kubectl create -f https://srossross.github.io/k8s-test-controller/controller.yaml 23 | kubectl --namespace kube-system rollout status deploy test-controller-deployment --watch 24 | ``` 25 | 26 | That's it! Now you can get started running tests. This controller adds two 27 | custom resources to the Kubernetes cluster - A `TestTemplate` and a `TestRun`. 28 | 29 | 30 | ## Resources 31 | 32 | ### Kind: TestTemplate 33 | 34 | A `TestTemplate` resource will look something like this: 35 | 36 | ```yaml 37 | # File test-success.yaml 38 | apiVersion: srossross.github.io/v1alpha1 39 | kind: TestTemplate 40 | metadata: 41 | name: test-success 42 | labels: # This can be used to filter tests in a testrun 43 | app: mytest 44 | spec: 45 | template: # This is just like a Kubernetes Job or Deployment template 46 | spec: 47 | containers: 48 | - name: alpine 49 | image: alpine 50 | command: [echo, hello] 51 | restartPolicy: Never 52 | ``` 53 | 54 | It will contain a `Pod` definition in the `spec.template` field. The `TestTemplate` will 55 | will instantiate this pod when a new `TestRun` is created. 56 | 57 | To add this test to your cluster, first create the file `./test-success.yaml` then run: 58 | 59 | ``` 60 | kubectl create -f ./test-success.yaml 61 | ``` 62 | 63 | A `TestTemplate` is comparable to a Kubernetes `[Job](https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/)`. 64 | Unlike a `Job`, a `TestTemplate` will not run by itself. For that you will need to create 65 | a `TestRun`. 66 | 67 | ### Kind: TestRun 68 | 69 | Creating a `TestRun` will instantiate all `Tests` that are matched by its 70 | optional `selector`. 71 | If the selector is omitted, all tests in the namespace will be run. 72 | 73 | ```yaml 74 | # File: test-run.yaml 75 | apiVersion: srossross.github.io/v1alpha1 76 | kind: TestRun 77 | metadata: 78 | name: test-run-1 79 | spec: 80 | max-jobs: 1 # The maximum number of test jobs to run simultaneously 81 | selector: # Optional -- This will filter the tests to run. 82 | matchLabels: 83 | app: mytest 84 | ``` 85 | 86 | To add this to your cluster, first create the file `test-run.yaml` and trigger 87 | test runs with: 88 | 89 | ``` 90 | kubectl create -f ./test-run.yaml 91 | ``` 92 | 93 | The controller will now start running your tests. 94 | 95 | ## Inspecting a TestRun 96 | 97 | A TestRun will emit Kubernetes events. You can inspect these events by either running 98 | `kubectl describe`: 99 | 100 | ``` 101 | kubectl describe testrun test-run-1 102 | ``` 103 | 104 | Or inspecting the events directly: 105 | 106 | ``` 107 | kubectl get events -l test-run=test-run-1 --watch 108 | ``` 109 | 110 | ### Monitoring Tests with runner.sh: 111 | 112 | 113 | For now you can use our bash script. This script will create and watch a TestRun and wait until it is finished: 114 | 115 | ```sh 116 | curl --fail https://srossross.github.io/k8s-test-controller/runner.sh > ./runner.sh 117 | bash ./runner.sh test-run-2 118 | ``` 119 | 120 | 121 | Waiting on [This CRD proposal](https://github.com/kubernetes/kubernetes/issues/38113) to be able to run: 122 | 123 | ```sh 124 | # I wish! But this will not work ... yet 125 | kubectl rollout status testrun test-run-1 --watch 126 | ``` 127 | 128 | 129 | ## Motivation Pt2 130 | 131 | ### Comparison with helm tests 132 | 133 | This test controller works great with helm. 134 | You can create **TestTemplate** and **TestRun** resources from your charts, the only 135 | difference is how a test is launched. you can now use `kubectl create -f testrun.yaml` 136 | instead of `helm test`. 137 | 138 | This test controller can be used in any k8s cluster without requiring helm. 139 | 140 | ### Tests are kubernetes resources. 141 | 142 | This means you can update your tests when you update your deployments. 143 | 144 | For example if you have a CI/CD pipeline that pushes a new image to your 145 | micro-service e.g.: 146 | 147 | ``` 148 | kubectl set image deploy mydeploy *=srossross/mynewimage 149 | ``` 150 | 151 | You may also want to update the tests that get run: 152 | 153 | ``` 154 | kubectl patch test test-mydeploy -p '{"spec":{"template":{"spec":{"containers":[{"name":"server","image":"srossross/mynewimage.test"}]}}}}' 155 | ``` 156 | 157 | ### TestRuns can filter out tests. 158 | 159 | In our CI/CD scenario deploying to our cluster, you may only want to run tests 160 | that are pertinent to the updated components, rather than all of the tests 161 | in your cluster. 162 | 163 | You can do this with `TestRun` selectors. Lets say we created our resources with 164 | helm, and we labeled all resources in a chart with `chart=mychart` 165 | 166 | e.g: 167 | 168 | ```yaml 169 | # File: test-run.yaml 170 | apiVersion: srossross.github.io/v1alpha1 171 | kind: TestRun 172 | metadata: 173 | name: test-run-1 174 | spec: 175 | selector: 176 | matchLabels: 177 | chart: mychart 178 | ``` 179 | 180 | 181 | 182 | --- 183 | 184 | 185 | ## Example TestTemplate 186 | 187 | In `wordpress/tests/test-mariadb-connection.yaml`: 188 | 189 | ``` 190 | apiVersion: srossross.github.io/v1alpha1 191 | kind: TestTemplate 192 | metadata: 193 | name: "credentials-test" 194 | labels: 195 | app: test 196 | spec: 197 | template: 198 | spec: 199 | containers: 200 | - name: credentials-test 201 | image: mariadb 202 | env: 203 | - name: MARIADB_HOST 204 | value: mariadb 205 | - name: MARIADB_PORT 206 | value: "3306" 207 | - name: WORDPRESS_DATABASE_NAME 208 | value: wordpress 209 | - name: WORDPRESS_DATABASE_USER 210 | value: root 211 | - name: WORDPRESS_DATABASE_PASSWORD 212 | valueFrom: 213 | secretKeyRef: 214 | name: mariadb-secrets 215 | key: mariadb-password 216 | command: ["sh", "-c", "mysql --host=$MARIADB_HOST --port=$MARIADB_PORT --user=$WORDPRESS_DATABASE_USER --password=$WORDPRESS_DATABASE_PASSWORD"] 217 | restartPolicy: Never 218 | ``` 219 | 220 | ### Steps to Run a Test Suite on this Resource 221 | 222 | 1. ```sh 223 | $ cat <&2 6 | } 7 | 8 | boom (){ 9 | echo_stderr 10 | echo_stderr " 💥 $*" 11 | echo_stderr 12 | exit 1 13 | } 14 | 15 | 16 | POSITIONAL=() 17 | 18 | NAMESPACE="default" 19 | SELECTORS="" 20 | while [[ $# -gt 0 ]] 21 | do 22 | key="$1" 23 | 24 | case $key in 25 | -h|--help) 26 | DO_HELP="yes" 27 | shift # past argument 28 | ;; 29 | --namespace) 30 | NAMESPACE="$2" 31 | shift # past argument 32 | shift # past value 33 | ;; 34 | -s|--selectors) 35 | SELECTORS="$2" 36 | shift # past argument 37 | shift # past value 38 | ;; 39 | *) # unknown option 40 | POSITIONAL+=("$1") # save it in an array for later 41 | shift # past argument 42 | ;; 43 | esac 44 | done 45 | 46 | set -- "${POSITIONAL[@]}" # restore positional parameters 47 | 48 | if [ ! -z "${DO_HELP+x}" ]; then 49 | cat < /dev/null; then 91 | echo 92 | else 93 | boom "Could not find test $TEST_NAME" 94 | fi 95 | 96 | ${KUBECTL} get ev -l test-run=${TEST_NAME} --watch -o 'go-template={{.reason}} {{.message}} 97 | ' & 98 | child=$! 99 | sleep 1 100 | 101 | until [[ $(${KUBECTL} get testruns ${TEST_NAME} -o jsonpath='{.status.status}') == "Complete" ]]; do 102 | sleep 5; 103 | done 104 | 105 | kill -TERM "$child" 2>/dev/null 106 | 107 | 108 | MESSAGE=$(${KUBECTL} get testruns ${TEST_NAME} -o jsonpath='{.status.message}') 109 | if [[ $(${KUBECTL} get testruns ${TEST_NAME} -o jsonpath='{.status.success}') == "true" ]]; then 110 | echo "Success: ${MESSAGE}" 111 | else 112 | boom "Fail: ${MESSAGE}" 113 | fi 114 | -------------------------------------------------------------------------------- /docs/test-run.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: srossross.github.io/v1alpha1 2 | kind: TestRun 3 | metadata: 4 | name: run-3 5 | spec: 6 | max-jobs: 2 7 | -------------------------------------------------------------------------------- /docs/tests.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: srossross.github.io/v1alpha1 2 | kind: TestTemplate 3 | metadata: 4 | name: test-success 5 | labels: 6 | app: test 7 | spec: 8 | template: 9 | metadata: 10 | labels: 11 | app: test 12 | spec: 13 | containers: 14 | - name: nginx 15 | image: nginx 16 | command: [echo, hello] 17 | restartPolicy: Never 18 | backoffLimit: 4 19 | --- 20 | apiVersion: srossross.github.io/v1alpha1 21 | kind: TestTemplate 22 | metadata: 23 | name: test-fail 24 | labels: 25 | app: test 26 | spec: 27 | template: 28 | metadata: 29 | labels: 30 | app: test 31 | spec: 32 | containers: 33 | - name: nginx 34 | image: nginx 35 | command: [sh, -c, "exit 1"] 36 | restartPolicy: Never 37 | backoffLimit: 4 38 | --- 39 | apiVersion: srossross.github.io/v1alpha1 40 | kind: TestTemplate 41 | metadata: 42 | name: test-sleep 43 | labels: 44 | app: test 45 | spec: 46 | template: 47 | metadata: 48 | labels: 49 | app: test 50 | spec: 51 | containers: 52 | - name: nginx 53 | image: nginx 54 | command: [sh, -c, "sleep 20"] 55 | restartPolicy: Never 56 | backoffLimit: 4 57 | -------------------------------------------------------------------------------- /hack/boilerplate.go.txt: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Sean Ross-Ross 6 | 7 | See License in the root of this repo. 8 | 9 | */ 10 | -------------------------------------------------------------------------------- /hack/update-client-gen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # The only argument this script should ever be called with is '--verify-only' 4 | 5 | set -o errexit 6 | set -o nounset 7 | set -o pipefail 8 | 9 | REPO_ROOT=$(dirname "${BASH_SOURCE}")/.. 10 | BINDIR=${REPO_ROOT}/bin 11 | 12 | # Generate the internal clientset (pkg/client/clientset_generated/internalclientset) 13 | ${BINDIR}/client-gen "$@" \ 14 | --input-base "github.com/srossross/k8s-test-controller/pkg/apis/" \ 15 | --input "tester/" \ 16 | --clientset-path "github.com/srossross/k8s-test-controller/pkg/client/" \ 17 | --clientset-name internalclientset \ 18 | --go-header-file "${GOPATH}/src/github.com/srossross/k8s-test-controller/hack/boilerplate.go.txt" 19 | # Generate the versioned clientset (pkg/client/clientset_generated/clientset) 20 | ${BINDIR}/client-gen "$@" \ 21 | --input-base "github.com/srossross/k8s-test-controller/pkg/apis/" \ 22 | --input "tester/v1alpha1" \ 23 | --clientset-path "github.com/srossross/k8s-test-controller/pkg/" \ 24 | --clientset-name "client" \ 25 | --go-header-file "${GOPATH}/src/github.com/srossross/k8s-test-controller/hack/boilerplate.go.txt" 26 | # generate lister 27 | ${BINDIR}/lister-gen "$@" \ 28 | --input-dirs="github.com/srossross/k8s-test-controller/pkg/apis/tester" \ 29 | --input-dirs="github.com/srossross/k8s-test-controller/pkg/apis/tester/v1alpha1" \ 30 | --output-package "github.com/srossross/k8s-test-controller/pkg/listers" \ 31 | --go-header-file "${GOPATH}/src/github.com/srossross/k8s-test-controller/hack/boilerplate.go.txt" 32 | # generate informer 33 | ${BINDIR}/informer-gen "$@" \ 34 | --go-header-file "${GOPATH}/src/github.com/srossross/k8s-test-controller/hack/boilerplate.go.txt" \ 35 | --input-dirs "github.com/srossross/k8s-test-controller/pkg/apis/tester" \ 36 | --input-dirs "github.com/srossross/k8s-test-controller/pkg/apis/tester/v1alpha1" \ 37 | --internal-clientset-package "github.com/srossross/k8s-test-controller/pkg/client/internalclientset" \ 38 | --versioned-clientset-package "github.com/srossross/k8s-test-controller/pkg/client" \ 39 | --listers-package "github.com/srossross/k8s-test-controller/pkg/listers" \ 40 | --output-package "github.com/srossross/k8s-test-controller/pkg/informers" 41 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "log" 6 | "time" 7 | 8 | apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" 9 | typedv1 "k8s.io/client-go/kubernetes/typed/core/v1" 10 | rest "k8s.io/client-go/rest" 11 | cache "k8s.io/client-go/tools/cache" 12 | clientcmd "k8s.io/client-go/tools/clientcmd" 13 | workqueue "k8s.io/client-go/util/workqueue" 14 | 15 | client "github.com/srossross/k8s-test-controller/pkg/client" 16 | controller "github.com/srossross/k8s-test-controller/pkg/controller" 17 | factory "github.com/srossross/k8s-test-controller/pkg/informers/externalversions" 18 | loop "github.com/srossross/k8s-test-controller/pkg/loop" 19 | run "github.com/srossross/k8s-test-controller/pkg/run" 20 | ) 21 | 22 | var ( 23 | 24 | // Version of this program (injected from linkflags) 25 | Version string 26 | 27 | // BuildTime of this program (injected from linkflags) 28 | BuildTime string 29 | 30 | // apiserverURL is the URL of the API server to connect to 31 | kubeconfig = flag.String("kubeconfig", "", "Path to a kubeconfig file") 32 | // pushbulletToken is the pushbullet API token to use 33 | // pushbulletToken = flag.String("pushbullet-token", "", "the api token to use to send pushbullet messages") 34 | 35 | // queue is a queue of resources to be processed. It performs exponential 36 | // backoff rate limiting, with a minimum retry period of 5 seconds and a 37 | // maximum of 1 minute. 38 | rateLimiter = workqueue.NewItemExponentialFailureRateLimiter(time.Second*5, time.Minute) 39 | queue = workqueue.NewRateLimitingQueue(rateLimiter) 40 | 41 | config *rest.Config 42 | // stopCh can be used to stop all the informer, as well as control loops 43 | // within the application. 44 | stopCh = make(chan struct{}) 45 | 46 | // sharedFactory is a shared informer factory that is used a a cache for 47 | // items in the API server. It saves each informer listing and watching the 48 | // same resources independently of each other, thus providing more up to 49 | // date results with less 'effort' 50 | sharedFactory factory.SharedInformerFactory 51 | 52 | ctrl controller.Interface 53 | 54 | // cl is a Kubernetes API client for our custom resource definition type 55 | cl *client.Clientset 56 | coreV1Client *typedv1.CoreV1Client 57 | 58 | // pb is the pushbullet client to use to send alerts 59 | // pb *pushbullet.Pushbullet 60 | ) 61 | 62 | func main() { 63 | flag.Parse() 64 | 65 | // TODO: add proper linker flags 66 | log.Printf("Test controller version: %s", Version) 67 | log.Printf(" Built on: %s", BuildTime) 68 | 69 | var err error 70 | 71 | config, err = GetClientConfig(*kubeconfig) 72 | 73 | if err != nil { 74 | log.Fatalf("error creating config: %s", err.Error()) 75 | } 76 | 77 | apiextensionsclientset, err := apiextensionsclient.NewForConfig(config) 78 | if err != nil { 79 | log.Fatalf("error creating api client: %s", err.Error()) 80 | } 81 | 82 | err = run.InstallAllCRDs(apiextensionsclientset) 83 | 84 | if err != nil { 85 | log.Fatalf("error creating crds: %s", err.Error()) 86 | } 87 | 88 | // create an instance of our own API client 89 | cl, err = client.NewForConfig(config) 90 | 91 | if err != nil { 92 | log.Fatalf("error creating api client: %s", err.Error()) 93 | } 94 | 95 | coreV1Client, err = typedv1.NewForConfig(config) 96 | 97 | if err != nil { 98 | log.Fatalf("error creating api client: %s", err.Error()) 99 | } 100 | 101 | log.Printf("Created Kubernetes client.") 102 | 103 | // we use a shared informer from the informer factory, to save calls to the 104 | // API as we grow our application and so state is consistent between our 105 | // control loops. We set a resync period of 30 seconds, in case any 106 | // create/replace/update/delete operations are missed when watching 107 | sharedFactory = factory.NewSharedInformerFactory(cl, time.Second*30) 108 | ctrl = controller.NewTestController(&sharedFactory, cl, coreV1Client) 109 | 110 | testRunInformer := run.NewTestRunInformer(sharedFactory, queue) 111 | 112 | testInformer := run.NewTestInformer(sharedFactory, queue) 113 | 114 | podInformer := run.SetupPodInformer(ctrl.PodInformer(), queue) 115 | 116 | // start the informer. This will cause it to begin receiving updates from 117 | // the configured API server and firing event handlers in response. 118 | sharedFactory.Start(stopCh) 119 | log.Printf("Started informer factory.") 120 | 121 | // wait for the informe rcache to finish performing it's initial sync of 122 | // resources 123 | if !cache.WaitForCacheSync(stopCh, testRunInformer.HasSynced) { 124 | log.Fatalf("error waiting for testRunInformer cache to sync: %s", err.Error()) 125 | } 126 | 127 | if !cache.WaitForCacheSync(stopCh, testInformer.HasSynced) { 128 | log.Fatalf("error waiting for testInformer cache to sync: %s", err.Error()) 129 | } 130 | 131 | if !cache.WaitForCacheSync(stopCh, podInformer.HasSynced) { 132 | log.Fatalf("error waiting for podInformer cache to sync: %s", err.Error()) 133 | } 134 | 135 | log.Printf("Finished populating shared informer cache.") 136 | // here we start just one worker reading objects off the queue. If you 137 | // wanted to parallelize this, you could start many instances of the worker 138 | // function, then ensure your application handles concurrency correctly. 139 | loop.Work(ctrl, run.New(), stopCh, queue) 140 | } 141 | 142 | // GetClientConfig gets config from command line kubeconfig param or InClusterConfig 143 | func GetClientConfig(kubeconfig string) (*rest.Config, error) { 144 | if kubeconfig != "" { 145 | return clientcmd.BuildConfigFromFlags("", kubeconfig) 146 | } 147 | return rest.InClusterConfig() 148 | } 149 | -------------------------------------------------------------------------------- /pkg/apis/tester/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 The Kubernetes Authors. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | 14 | // +k8s:deepcopy-gen=package,register 15 | // +groupName=srossross.github.io 16 | 17 | // Package tester is the internal version of the API. 18 | package tester 19 | -------------------------------------------------------------------------------- /pkg/apis/tester/install/install.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 The Kubernetes Authors. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | 14 | // Package install installs the experimental API group, making it available as 15 | // an option to all of the API encoding/decoding machinery. 16 | package install 17 | 18 | import ( 19 | "k8s.io/apimachinery/pkg/apimachinery/announced" 20 | "k8s.io/apimachinery/pkg/apimachinery/registered" 21 | "k8s.io/apimachinery/pkg/runtime" 22 | 23 | "github.com/srossross/k8s-test-controller/pkg/apis/tester" 24 | "github.com/srossross/k8s-test-controller/pkg/apis/tester/v1alpha1" 25 | ) 26 | 27 | // Install registers the API group and adds types to a scheme 28 | func Install(groupFactoryRegistry announced.APIGroupFactoryRegistry, registry *registered.APIRegistrationManager, scheme *runtime.Scheme) { 29 | if err := announced.NewGroupMetaFactory( 30 | &announced.GroupMetaFactoryArgs{ 31 | GroupName: tester.GroupName, 32 | VersionPreferenceOrder: []string{v1alpha1.SchemeGroupVersion.Version}, 33 | AddInternalObjectsToScheme: tester.AddToScheme, 34 | }, 35 | announced.VersionToSchemeFunc{ 36 | v1alpha1.SchemeGroupVersion.Version: v1alpha1.AddToScheme, 37 | }, 38 | ).Announce(groupFactoryRegistry).RegisterAndEnable(registry, scheme); err != nil { 39 | panic(err) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /pkg/apis/tester/register.go: -------------------------------------------------------------------------------- 1 | package tester 2 | 3 | import ( 4 | "k8s.io/apimachinery/pkg/runtime" 5 | "k8s.io/apimachinery/pkg/runtime/schema" 6 | ) 7 | 8 | // GroupName is the group name use in this package 9 | const GroupName = "srossross.github.io" 10 | 11 | // SchemeGroupVersion is group version used to register these objects 12 | var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal} 13 | 14 | // Kind takes an unqualified kind and returns a Group qualified GroupKind 15 | func Kind(kind string) schema.GroupKind { 16 | return SchemeGroupVersion.WithKind(kind).GroupKind() 17 | } 18 | 19 | // Resource takes an unqualified resource and returns a Group qualified GroupResource 20 | func Resource(resource string) schema.GroupResource { 21 | return SchemeGroupVersion.WithResource(resource).GroupResource() 22 | } 23 | 24 | var ( 25 | SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) 26 | AddToScheme = SchemeBuilder.AddToScheme 27 | ) 28 | 29 | // Adds the list of known types to api.Scheme. 30 | func addKnownTypes(scheme *runtime.Scheme) error { 31 | scheme.AddKnownTypes(SchemeGroupVersion, 32 | &TestRun{}, 33 | &TestRunList{}, 34 | &TestTemplate{}, 35 | &TestTemplateList{}, 36 | ) 37 | return nil 38 | } 39 | -------------------------------------------------------------------------------- /pkg/apis/tester/types.go: -------------------------------------------------------------------------------- 1 | package tester 2 | 3 | import ( 4 | corev1 "k8s.io/api/core/v1" 5 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 6 | ) 7 | 8 | // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object 9 | 10 | type TestRun struct { 11 | metav1.TypeMeta 12 | metav1.ObjectMeta 13 | 14 | Spec TestRunSpec 15 | Status TestRunStatus 16 | } 17 | 18 | // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object 19 | 20 | type TestRunList struct { 21 | metav1.TypeMeta 22 | metav1.ListMeta 23 | 24 | Items []TestRun 25 | } 26 | 27 | type TestRunSpec struct { 28 | Selector *metav1.LabelSelector 29 | MaxJobs int 30 | MaxFail int 31 | } 32 | 33 | type TestRunRecord struct { 34 | TestName string 35 | PodRef *corev1.ObjectReference 36 | StartTime *metav1.Time 37 | EndTime *metav1.Time 38 | Result string 39 | } 40 | 41 | type TestRunStatus struct { 42 | Status string 43 | Message string 44 | Success bool 45 | Records []TestRunRecord 46 | } 47 | 48 | // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object 49 | 50 | type TestTemplate struct { 51 | metav1.TypeMeta 52 | metav1.ObjectMeta 53 | 54 | Spec TestTemplateSpec 55 | } 56 | 57 | // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object 58 | 59 | type TestTemplateList struct { 60 | metav1.TypeMeta 61 | metav1.ListMeta 62 | 63 | Items []TestTemplate 64 | } 65 | 66 | type TestTemplateSpec struct { 67 | Description string 68 | Weight int 69 | Template corev1.PodTemplateSpec 70 | } 71 | -------------------------------------------------------------------------------- /pkg/apis/tester/v1alpha1/conversion.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import ( 4 | "k8s.io/apimachinery/pkg/runtime" 5 | ) 6 | 7 | func addConversionFuncs(scheme *runtime.Scheme) error { 8 | // Add non-generated conversion functions 9 | return scheme.AddConversionFuncs() 10 | } -------------------------------------------------------------------------------- /pkg/apis/tester/v1alpha1/defaults.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import ( 4 | "k8s.io/apimachinery/pkg/runtime" 5 | ) 6 | 7 | func addDefaultingFuncs(scheme *runtime.Scheme) error { 8 | return RegisterDefaults(scheme) 9 | } 10 | -------------------------------------------------------------------------------- /pkg/apis/tester/v1alpha1/doc.go: -------------------------------------------------------------------------------- 1 | // +k8s:deepcopy-gen=package,register 2 | // +k8s:conversion-gen=github.com/srossross/k8s-test-controller/pkg/apis/tester 3 | // +k8s:openapi-gen=true 4 | // +k8s:defaulter-gen=TypeMeta 5 | 6 | // Package v1alpha1 is the v1alpha1 version of the API. 7 | // +groupName=srossross.github.io 8 | package v1alpha1 9 | -------------------------------------------------------------------------------- /pkg/apis/tester/v1alpha1/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 The Kubernetes Authors. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | 14 | package v1alpha1 15 | 16 | import ( 17 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 18 | "k8s.io/apimachinery/pkg/runtime" 19 | "k8s.io/apimachinery/pkg/runtime/schema" 20 | 21 | "github.com/srossross/k8s-test-controller/pkg/apis/tester" 22 | ) 23 | 24 | // SchemeGroupVersion is group version used to register these objects 25 | var SchemeGroupVersion = schema.GroupVersion{Group: tester.GroupName, Version: "v1alpha1"} 26 | 27 | // Resource takes an unqualified resource and returns a Group qualified GroupResource 28 | func Resource(resource string) schema.GroupResource { 29 | return SchemeGroupVersion.WithResource(resource).GroupResource() 30 | } 31 | 32 | var ( 33 | // TODO: move SchemeBuilder with zz_generated.deepcopy.go to k8s.io/api. 34 | // localSchemeBuilder and AddToScheme will stay in k8s.io/kubernetes. 35 | SchemeBuilder runtime.SchemeBuilder 36 | localSchemeBuilder = &SchemeBuilder 37 | AddToScheme = localSchemeBuilder.AddToScheme 38 | ) 39 | 40 | func init() { 41 | // We only register manually written functions here. The registration of the 42 | // generated functions takes place in the generated files. The separation 43 | // makes the code compile even when the generated files are missing. 44 | localSchemeBuilder.Register(addKnownTypes, addDefaultingFuncs, addConversionFuncs) 45 | } 46 | 47 | // Adds the list of known types to api.Scheme. 48 | func addKnownTypes(scheme *runtime.Scheme) error { 49 | scheme.AddKnownTypes(SchemeGroupVersion, 50 | &TestRun{}, 51 | &TestRunList{}, 52 | &TestTemplate{}, 53 | &TestTemplateList{}, 54 | ) 55 | metav1.AddToGroupVersion(scheme, SchemeGroupVersion) 56 | return nil 57 | } 58 | -------------------------------------------------------------------------------- /pkg/apis/tester/v1alpha1/types.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import ( 4 | corev1 "k8s.io/api/core/v1" 5 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 6 | ) 7 | 8 | var ( 9 | // TestRunComplete will set the status to complete 10 | TestRunComplete = "Complete" 11 | TestRunRunning = "Running" 12 | ) 13 | 14 | // +genclient=true 15 | // +genclient=nonNamespaced 16 | // +k8s:openapi-gen=true 17 | // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object 18 | // +resource:path=testruns 19 | 20 | type TestRun struct { 21 | metav1.TypeMeta `json:",inline"` 22 | metav1.ObjectMeta `json:"metadata,omitempty"` 23 | 24 | Spec TestRunSpec `json:"spec,omitempty"` 25 | Status TestRunStatus `json:"status,omitempty"` 26 | } 27 | 28 | // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object 29 | 30 | type TestRunList struct { 31 | metav1.TypeMeta `json:",inline"` 32 | metav1.ListMeta `json:"metadata,omitempty"` 33 | 34 | Items []TestRun `json:"items"` 35 | } 36 | 37 | type TestRunSpec struct { 38 | // Label selector for pods. Existing ReplicaSets whose pods are 39 | // selected by this will be the ones affected by this deployment. 40 | // +optional 41 | Selector *metav1.LabelSelector `json:"selector,omitempty"` 42 | 43 | // The Maximum number of pods to run symultaniously 44 | MaxJobs int `json:"max-jobs"` 45 | 46 | // The Maximum number of failures before stoping the test run 47 | // and mark it as a failure 48 | MaxFail int `json:"maxfail"` 49 | } 50 | 51 | // TestRunRecord is a refrence to a pod run 52 | type TestRunRecord struct { 53 | 54 | // the name of the test to run 55 | TestName string `json:"testname"` 56 | // The pod that this run 57 | PodRef *corev1.ObjectReference `json:"podref"` 58 | // When the pod was started 59 | StartTime *metav1.Time `json:"starttime"` 60 | // When the pod was started 61 | EndTime *metav1.Time `json:"endtime"` 62 | 63 | // When the pod was started 64 | Result string `json:"result"` 65 | } 66 | 67 | type TestRunStatus struct { 68 | Status string `json:"status"` 69 | Message string `json:"message"` 70 | Success bool `json:"success"` 71 | 72 | Records []TestRunRecord `json:"records"` 73 | } 74 | 75 | // +genclient=true 76 | // +genclient=nonNamespaced 77 | // +genclient=noStatus 78 | // +k8s:openapi-gen=true 79 | // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object 80 | // +resource:path=testtemplates 81 | 82 | type TestTemplate struct { 83 | metav1.TypeMeta `json:",inline"` 84 | metav1.ObjectMeta `json:"metadata,omitempty"` 85 | 86 | Spec TestTemplateSpec `json:"spec,omitempty"` 87 | } 88 | 89 | // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object 90 | 91 | type TestTemplateList struct { 92 | metav1.TypeMeta `json:",inline"` 93 | metav1.ListMeta `json:"metadata,omitempty"` 94 | 95 | Items []TestTemplate `json:"items"` 96 | } 97 | 98 | type TestTemplateSpec struct { 99 | 100 | // Description of what the test is about 101 | Description string `json:"description"` 102 | // Test run weight. the pods will be run in sorted order of (Weight, Name) 103 | Weight int `json:"weight"` 104 | Template corev1.PodTemplateSpec `json:"template"` 105 | } 106 | -------------------------------------------------------------------------------- /pkg/apis/tester/v1alpha1/zz_generated.conversion.go: -------------------------------------------------------------------------------- 1 | // +build !ignore_autogenerated 2 | 3 | /* 4 | 5 | MIT License 6 | 7 | Copyright (c) 2017 Sean Ross-Ross 8 | 9 | See License in the root of this repo. 10 | 11 | */ 12 | 13 | // This file was autogenerated by conversion-gen. Do not edit it manually! 14 | 15 | package v1alpha1 16 | 17 | import ( 18 | tester "github.com/srossross/k8s-test-controller/pkg/apis/tester" 19 | v1 "k8s.io/api/core/v1" 20 | meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 21 | conversion "k8s.io/apimachinery/pkg/conversion" 22 | runtime "k8s.io/apimachinery/pkg/runtime" 23 | unsafe "unsafe" 24 | ) 25 | 26 | func init() { 27 | localSchemeBuilder.Register(RegisterConversions) 28 | } 29 | 30 | // RegisterConversions adds conversion functions to the given scheme. 31 | // Public to allow building arbitrary schemes. 32 | func RegisterConversions(scheme *runtime.Scheme) error { 33 | return scheme.AddGeneratedConversionFuncs( 34 | Convert_v1alpha1_TestRun_To_tester_TestRun, 35 | Convert_tester_TestRun_To_v1alpha1_TestRun, 36 | Convert_v1alpha1_TestRunList_To_tester_TestRunList, 37 | Convert_tester_TestRunList_To_v1alpha1_TestRunList, 38 | Convert_v1alpha1_TestRunRecord_To_tester_TestRunRecord, 39 | Convert_tester_TestRunRecord_To_v1alpha1_TestRunRecord, 40 | Convert_v1alpha1_TestRunSpec_To_tester_TestRunSpec, 41 | Convert_tester_TestRunSpec_To_v1alpha1_TestRunSpec, 42 | Convert_v1alpha1_TestRunStatus_To_tester_TestRunStatus, 43 | Convert_tester_TestRunStatus_To_v1alpha1_TestRunStatus, 44 | Convert_v1alpha1_TestTemplate_To_tester_TestTemplate, 45 | Convert_tester_TestTemplate_To_v1alpha1_TestTemplate, 46 | Convert_v1alpha1_TestTemplateList_To_tester_TestTemplateList, 47 | Convert_tester_TestTemplateList_To_v1alpha1_TestTemplateList, 48 | Convert_v1alpha1_TestTemplateSpec_To_tester_TestTemplateSpec, 49 | Convert_tester_TestTemplateSpec_To_v1alpha1_TestTemplateSpec, 50 | ) 51 | } 52 | 53 | func autoConvert_v1alpha1_TestRun_To_tester_TestRun(in *TestRun, out *tester.TestRun, s conversion.Scope) error { 54 | out.ObjectMeta = in.ObjectMeta 55 | if err := Convert_v1alpha1_TestRunSpec_To_tester_TestRunSpec(&in.Spec, &out.Spec, s); err != nil { 56 | return err 57 | } 58 | if err := Convert_v1alpha1_TestRunStatus_To_tester_TestRunStatus(&in.Status, &out.Status, s); err != nil { 59 | return err 60 | } 61 | return nil 62 | } 63 | 64 | // Convert_v1alpha1_TestRun_To_tester_TestRun is an autogenerated conversion function. 65 | func Convert_v1alpha1_TestRun_To_tester_TestRun(in *TestRun, out *tester.TestRun, s conversion.Scope) error { 66 | return autoConvert_v1alpha1_TestRun_To_tester_TestRun(in, out, s) 67 | } 68 | 69 | func autoConvert_tester_TestRun_To_v1alpha1_TestRun(in *tester.TestRun, out *TestRun, s conversion.Scope) error { 70 | out.ObjectMeta = in.ObjectMeta 71 | if err := Convert_tester_TestRunSpec_To_v1alpha1_TestRunSpec(&in.Spec, &out.Spec, s); err != nil { 72 | return err 73 | } 74 | if err := Convert_tester_TestRunStatus_To_v1alpha1_TestRunStatus(&in.Status, &out.Status, s); err != nil { 75 | return err 76 | } 77 | return nil 78 | } 79 | 80 | // Convert_tester_TestRun_To_v1alpha1_TestRun is an autogenerated conversion function. 81 | func Convert_tester_TestRun_To_v1alpha1_TestRun(in *tester.TestRun, out *TestRun, s conversion.Scope) error { 82 | return autoConvert_tester_TestRun_To_v1alpha1_TestRun(in, out, s) 83 | } 84 | 85 | func autoConvert_v1alpha1_TestRunList_To_tester_TestRunList(in *TestRunList, out *tester.TestRunList, s conversion.Scope) error { 86 | out.ListMeta = in.ListMeta 87 | out.Items = *(*[]tester.TestRun)(unsafe.Pointer(&in.Items)) 88 | return nil 89 | } 90 | 91 | // Convert_v1alpha1_TestRunList_To_tester_TestRunList is an autogenerated conversion function. 92 | func Convert_v1alpha1_TestRunList_To_tester_TestRunList(in *TestRunList, out *tester.TestRunList, s conversion.Scope) error { 93 | return autoConvert_v1alpha1_TestRunList_To_tester_TestRunList(in, out, s) 94 | } 95 | 96 | func autoConvert_tester_TestRunList_To_v1alpha1_TestRunList(in *tester.TestRunList, out *TestRunList, s conversion.Scope) error { 97 | out.ListMeta = in.ListMeta 98 | if in.Items == nil { 99 | out.Items = make([]TestRun, 0) 100 | } else { 101 | out.Items = *(*[]TestRun)(unsafe.Pointer(&in.Items)) 102 | } 103 | return nil 104 | } 105 | 106 | // Convert_tester_TestRunList_To_v1alpha1_TestRunList is an autogenerated conversion function. 107 | func Convert_tester_TestRunList_To_v1alpha1_TestRunList(in *tester.TestRunList, out *TestRunList, s conversion.Scope) error { 108 | return autoConvert_tester_TestRunList_To_v1alpha1_TestRunList(in, out, s) 109 | } 110 | 111 | func autoConvert_v1alpha1_TestRunRecord_To_tester_TestRunRecord(in *TestRunRecord, out *tester.TestRunRecord, s conversion.Scope) error { 112 | out.TestName = in.TestName 113 | out.PodRef = (*v1.ObjectReference)(unsafe.Pointer(in.PodRef)) 114 | out.StartTime = (*meta_v1.Time)(unsafe.Pointer(in.StartTime)) 115 | out.EndTime = (*meta_v1.Time)(unsafe.Pointer(in.EndTime)) 116 | out.Result = in.Result 117 | return nil 118 | } 119 | 120 | // Convert_v1alpha1_TestRunRecord_To_tester_TestRunRecord is an autogenerated conversion function. 121 | func Convert_v1alpha1_TestRunRecord_To_tester_TestRunRecord(in *TestRunRecord, out *tester.TestRunRecord, s conversion.Scope) error { 122 | return autoConvert_v1alpha1_TestRunRecord_To_tester_TestRunRecord(in, out, s) 123 | } 124 | 125 | func autoConvert_tester_TestRunRecord_To_v1alpha1_TestRunRecord(in *tester.TestRunRecord, out *TestRunRecord, s conversion.Scope) error { 126 | out.TestName = in.TestName 127 | out.PodRef = (*v1.ObjectReference)(unsafe.Pointer(in.PodRef)) 128 | out.StartTime = (*meta_v1.Time)(unsafe.Pointer(in.StartTime)) 129 | out.EndTime = (*meta_v1.Time)(unsafe.Pointer(in.EndTime)) 130 | out.Result = in.Result 131 | return nil 132 | } 133 | 134 | // Convert_tester_TestRunRecord_To_v1alpha1_TestRunRecord is an autogenerated conversion function. 135 | func Convert_tester_TestRunRecord_To_v1alpha1_TestRunRecord(in *tester.TestRunRecord, out *TestRunRecord, s conversion.Scope) error { 136 | return autoConvert_tester_TestRunRecord_To_v1alpha1_TestRunRecord(in, out, s) 137 | } 138 | 139 | func autoConvert_v1alpha1_TestRunSpec_To_tester_TestRunSpec(in *TestRunSpec, out *tester.TestRunSpec, s conversion.Scope) error { 140 | out.Selector = (*meta_v1.LabelSelector)(unsafe.Pointer(in.Selector)) 141 | out.MaxJobs = in.MaxJobs 142 | out.MaxFail = in.MaxFail 143 | return nil 144 | } 145 | 146 | // Convert_v1alpha1_TestRunSpec_To_tester_TestRunSpec is an autogenerated conversion function. 147 | func Convert_v1alpha1_TestRunSpec_To_tester_TestRunSpec(in *TestRunSpec, out *tester.TestRunSpec, s conversion.Scope) error { 148 | return autoConvert_v1alpha1_TestRunSpec_To_tester_TestRunSpec(in, out, s) 149 | } 150 | 151 | func autoConvert_tester_TestRunSpec_To_v1alpha1_TestRunSpec(in *tester.TestRunSpec, out *TestRunSpec, s conversion.Scope) error { 152 | out.Selector = (*meta_v1.LabelSelector)(unsafe.Pointer(in.Selector)) 153 | out.MaxJobs = in.MaxJobs 154 | out.MaxFail = in.MaxFail 155 | return nil 156 | } 157 | 158 | // Convert_tester_TestRunSpec_To_v1alpha1_TestRunSpec is an autogenerated conversion function. 159 | func Convert_tester_TestRunSpec_To_v1alpha1_TestRunSpec(in *tester.TestRunSpec, out *TestRunSpec, s conversion.Scope) error { 160 | return autoConvert_tester_TestRunSpec_To_v1alpha1_TestRunSpec(in, out, s) 161 | } 162 | 163 | func autoConvert_v1alpha1_TestRunStatus_To_tester_TestRunStatus(in *TestRunStatus, out *tester.TestRunStatus, s conversion.Scope) error { 164 | out.Status = in.Status 165 | out.Message = in.Message 166 | out.Success = in.Success 167 | out.Records = *(*[]tester.TestRunRecord)(unsafe.Pointer(&in.Records)) 168 | return nil 169 | } 170 | 171 | // Convert_v1alpha1_TestRunStatus_To_tester_TestRunStatus is an autogenerated conversion function. 172 | func Convert_v1alpha1_TestRunStatus_To_tester_TestRunStatus(in *TestRunStatus, out *tester.TestRunStatus, s conversion.Scope) error { 173 | return autoConvert_v1alpha1_TestRunStatus_To_tester_TestRunStatus(in, out, s) 174 | } 175 | 176 | func autoConvert_tester_TestRunStatus_To_v1alpha1_TestRunStatus(in *tester.TestRunStatus, out *TestRunStatus, s conversion.Scope) error { 177 | out.Status = in.Status 178 | out.Message = in.Message 179 | out.Success = in.Success 180 | if in.Records == nil { 181 | out.Records = make([]TestRunRecord, 0) 182 | } else { 183 | out.Records = *(*[]TestRunRecord)(unsafe.Pointer(&in.Records)) 184 | } 185 | return nil 186 | } 187 | 188 | // Convert_tester_TestRunStatus_To_v1alpha1_TestRunStatus is an autogenerated conversion function. 189 | func Convert_tester_TestRunStatus_To_v1alpha1_TestRunStatus(in *tester.TestRunStatus, out *TestRunStatus, s conversion.Scope) error { 190 | return autoConvert_tester_TestRunStatus_To_v1alpha1_TestRunStatus(in, out, s) 191 | } 192 | 193 | func autoConvert_v1alpha1_TestTemplate_To_tester_TestTemplate(in *TestTemplate, out *tester.TestTemplate, s conversion.Scope) error { 194 | out.ObjectMeta = in.ObjectMeta 195 | if err := Convert_v1alpha1_TestTemplateSpec_To_tester_TestTemplateSpec(&in.Spec, &out.Spec, s); err != nil { 196 | return err 197 | } 198 | return nil 199 | } 200 | 201 | // Convert_v1alpha1_TestTemplate_To_tester_TestTemplate is an autogenerated conversion function. 202 | func Convert_v1alpha1_TestTemplate_To_tester_TestTemplate(in *TestTemplate, out *tester.TestTemplate, s conversion.Scope) error { 203 | return autoConvert_v1alpha1_TestTemplate_To_tester_TestTemplate(in, out, s) 204 | } 205 | 206 | func autoConvert_tester_TestTemplate_To_v1alpha1_TestTemplate(in *tester.TestTemplate, out *TestTemplate, s conversion.Scope) error { 207 | out.ObjectMeta = in.ObjectMeta 208 | if err := Convert_tester_TestTemplateSpec_To_v1alpha1_TestTemplateSpec(&in.Spec, &out.Spec, s); err != nil { 209 | return err 210 | } 211 | return nil 212 | } 213 | 214 | // Convert_tester_TestTemplate_To_v1alpha1_TestTemplate is an autogenerated conversion function. 215 | func Convert_tester_TestTemplate_To_v1alpha1_TestTemplate(in *tester.TestTemplate, out *TestTemplate, s conversion.Scope) error { 216 | return autoConvert_tester_TestTemplate_To_v1alpha1_TestTemplate(in, out, s) 217 | } 218 | 219 | func autoConvert_v1alpha1_TestTemplateList_To_tester_TestTemplateList(in *TestTemplateList, out *tester.TestTemplateList, s conversion.Scope) error { 220 | out.ListMeta = in.ListMeta 221 | out.Items = *(*[]tester.TestTemplate)(unsafe.Pointer(&in.Items)) 222 | return nil 223 | } 224 | 225 | // Convert_v1alpha1_TestTemplateList_To_tester_TestTemplateList is an autogenerated conversion function. 226 | func Convert_v1alpha1_TestTemplateList_To_tester_TestTemplateList(in *TestTemplateList, out *tester.TestTemplateList, s conversion.Scope) error { 227 | return autoConvert_v1alpha1_TestTemplateList_To_tester_TestTemplateList(in, out, s) 228 | } 229 | 230 | func autoConvert_tester_TestTemplateList_To_v1alpha1_TestTemplateList(in *tester.TestTemplateList, out *TestTemplateList, s conversion.Scope) error { 231 | out.ListMeta = in.ListMeta 232 | if in.Items == nil { 233 | out.Items = make([]TestTemplate, 0) 234 | } else { 235 | out.Items = *(*[]TestTemplate)(unsafe.Pointer(&in.Items)) 236 | } 237 | return nil 238 | } 239 | 240 | // Convert_tester_TestTemplateList_To_v1alpha1_TestTemplateList is an autogenerated conversion function. 241 | func Convert_tester_TestTemplateList_To_v1alpha1_TestTemplateList(in *tester.TestTemplateList, out *TestTemplateList, s conversion.Scope) error { 242 | return autoConvert_tester_TestTemplateList_To_v1alpha1_TestTemplateList(in, out, s) 243 | } 244 | 245 | func autoConvert_v1alpha1_TestTemplateSpec_To_tester_TestTemplateSpec(in *TestTemplateSpec, out *tester.TestTemplateSpec, s conversion.Scope) error { 246 | out.Description = in.Description 247 | out.Weight = in.Weight 248 | out.Template = in.Template 249 | return nil 250 | } 251 | 252 | // Convert_v1alpha1_TestTemplateSpec_To_tester_TestTemplateSpec is an autogenerated conversion function. 253 | func Convert_v1alpha1_TestTemplateSpec_To_tester_TestTemplateSpec(in *TestTemplateSpec, out *tester.TestTemplateSpec, s conversion.Scope) error { 254 | return autoConvert_v1alpha1_TestTemplateSpec_To_tester_TestTemplateSpec(in, out, s) 255 | } 256 | 257 | func autoConvert_tester_TestTemplateSpec_To_v1alpha1_TestTemplateSpec(in *tester.TestTemplateSpec, out *TestTemplateSpec, s conversion.Scope) error { 258 | out.Description = in.Description 259 | out.Weight = in.Weight 260 | out.Template = in.Template 261 | return nil 262 | } 263 | 264 | // Convert_tester_TestTemplateSpec_To_v1alpha1_TestTemplateSpec is an autogenerated conversion function. 265 | func Convert_tester_TestTemplateSpec_To_v1alpha1_TestTemplateSpec(in *tester.TestTemplateSpec, out *TestTemplateSpec, s conversion.Scope) error { 266 | return autoConvert_tester_TestTemplateSpec_To_v1alpha1_TestTemplateSpec(in, out, s) 267 | } 268 | -------------------------------------------------------------------------------- /pkg/apis/tester/v1alpha1/zz_generated.deepcopy.go: -------------------------------------------------------------------------------- 1 | // +build !ignore_autogenerated 2 | 3 | /* 4 | 5 | MIT License 6 | 7 | Copyright (c) 2017 Sean Ross-Ross 8 | 9 | See License in the root of this repo. 10 | 11 | */ 12 | 13 | // This file was autogenerated by deepcopy-gen. Do not edit it manually! 14 | 15 | package v1alpha1 16 | 17 | import ( 18 | v1 "k8s.io/api/core/v1" 19 | meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 20 | conversion "k8s.io/apimachinery/pkg/conversion" 21 | runtime "k8s.io/apimachinery/pkg/runtime" 22 | reflect "reflect" 23 | ) 24 | 25 | // Deprecated: register deep-copy functions. 26 | func init() { 27 | SchemeBuilder.Register(RegisterDeepCopies) 28 | } 29 | 30 | // Deprecated: RegisterDeepCopies adds deep-copy functions to the given scheme. Public 31 | // to allow building arbitrary schemes. 32 | func RegisterDeepCopies(scheme *runtime.Scheme) error { 33 | return scheme.AddGeneratedDeepCopyFuncs( 34 | conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error { 35 | in.(*TestRun).DeepCopyInto(out.(*TestRun)) 36 | return nil 37 | }, InType: reflect.TypeOf(&TestRun{})}, 38 | conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error { 39 | in.(*TestRunList).DeepCopyInto(out.(*TestRunList)) 40 | return nil 41 | }, InType: reflect.TypeOf(&TestRunList{})}, 42 | conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error { 43 | in.(*TestRunRecord).DeepCopyInto(out.(*TestRunRecord)) 44 | return nil 45 | }, InType: reflect.TypeOf(&TestRunRecord{})}, 46 | conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error { 47 | in.(*TestRunSpec).DeepCopyInto(out.(*TestRunSpec)) 48 | return nil 49 | }, InType: reflect.TypeOf(&TestRunSpec{})}, 50 | conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error { 51 | in.(*TestRunStatus).DeepCopyInto(out.(*TestRunStatus)) 52 | return nil 53 | }, InType: reflect.TypeOf(&TestRunStatus{})}, 54 | conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error { 55 | in.(*TestTemplate).DeepCopyInto(out.(*TestTemplate)) 56 | return nil 57 | }, InType: reflect.TypeOf(&TestTemplate{})}, 58 | conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error { 59 | in.(*TestTemplateList).DeepCopyInto(out.(*TestTemplateList)) 60 | return nil 61 | }, InType: reflect.TypeOf(&TestTemplateList{})}, 62 | conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error { 63 | in.(*TestTemplateSpec).DeepCopyInto(out.(*TestTemplateSpec)) 64 | return nil 65 | }, InType: reflect.TypeOf(&TestTemplateSpec{})}, 66 | ) 67 | } 68 | 69 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 70 | func (in *TestRun) DeepCopyInto(out *TestRun) { 71 | *out = *in 72 | out.TypeMeta = in.TypeMeta 73 | in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) 74 | in.Spec.DeepCopyInto(&out.Spec) 75 | in.Status.DeepCopyInto(&out.Status) 76 | return 77 | } 78 | 79 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, creating a new TestRun. 80 | func (x *TestRun) DeepCopy() *TestRun { 81 | if x == nil { 82 | return nil 83 | } 84 | out := new(TestRun) 85 | x.DeepCopyInto(out) 86 | return out 87 | } 88 | 89 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 90 | func (x *TestRun) DeepCopyObject() runtime.Object { 91 | if c := x.DeepCopy(); c != nil { 92 | return c 93 | } else { 94 | return nil 95 | } 96 | } 97 | 98 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 99 | func (in *TestRunList) DeepCopyInto(out *TestRunList) { 100 | *out = *in 101 | out.TypeMeta = in.TypeMeta 102 | out.ListMeta = in.ListMeta 103 | if in.Items != nil { 104 | in, out := &in.Items, &out.Items 105 | *out = make([]TestRun, len(*in)) 106 | for i := range *in { 107 | (*in)[i].DeepCopyInto(&(*out)[i]) 108 | } 109 | } 110 | return 111 | } 112 | 113 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, creating a new TestRunList. 114 | func (x *TestRunList) DeepCopy() *TestRunList { 115 | if x == nil { 116 | return nil 117 | } 118 | out := new(TestRunList) 119 | x.DeepCopyInto(out) 120 | return out 121 | } 122 | 123 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 124 | func (x *TestRunList) DeepCopyObject() runtime.Object { 125 | if c := x.DeepCopy(); c != nil { 126 | return c 127 | } else { 128 | return nil 129 | } 130 | } 131 | 132 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 133 | func (in *TestRunRecord) DeepCopyInto(out *TestRunRecord) { 134 | *out = *in 135 | if in.PodRef != nil { 136 | in, out := &in.PodRef, &out.PodRef 137 | if *in == nil { 138 | *out = nil 139 | } else { 140 | *out = new(v1.ObjectReference) 141 | **out = **in 142 | } 143 | } 144 | if in.StartTime != nil { 145 | in, out := &in.StartTime, &out.StartTime 146 | if *in == nil { 147 | *out = nil 148 | } else { 149 | *out = new(meta_v1.Time) 150 | (*in).DeepCopyInto(*out) 151 | } 152 | } 153 | if in.EndTime != nil { 154 | in, out := &in.EndTime, &out.EndTime 155 | if *in == nil { 156 | *out = nil 157 | } else { 158 | *out = new(meta_v1.Time) 159 | (*in).DeepCopyInto(*out) 160 | } 161 | } 162 | return 163 | } 164 | 165 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, creating a new TestRunRecord. 166 | func (x *TestRunRecord) DeepCopy() *TestRunRecord { 167 | if x == nil { 168 | return nil 169 | } 170 | out := new(TestRunRecord) 171 | x.DeepCopyInto(out) 172 | return out 173 | } 174 | 175 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 176 | func (in *TestRunSpec) DeepCopyInto(out *TestRunSpec) { 177 | *out = *in 178 | if in.Selector != nil { 179 | in, out := &in.Selector, &out.Selector 180 | if *in == nil { 181 | *out = nil 182 | } else { 183 | *out = new(meta_v1.LabelSelector) 184 | (*in).DeepCopyInto(*out) 185 | } 186 | } 187 | return 188 | } 189 | 190 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, creating a new TestRunSpec. 191 | func (x *TestRunSpec) DeepCopy() *TestRunSpec { 192 | if x == nil { 193 | return nil 194 | } 195 | out := new(TestRunSpec) 196 | x.DeepCopyInto(out) 197 | return out 198 | } 199 | 200 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 201 | func (in *TestRunStatus) DeepCopyInto(out *TestRunStatus) { 202 | *out = *in 203 | if in.Records != nil { 204 | in, out := &in.Records, &out.Records 205 | *out = make([]TestRunRecord, len(*in)) 206 | for i := range *in { 207 | (*in)[i].DeepCopyInto(&(*out)[i]) 208 | } 209 | } 210 | return 211 | } 212 | 213 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, creating a new TestRunStatus. 214 | func (x *TestRunStatus) DeepCopy() *TestRunStatus { 215 | if x == nil { 216 | return nil 217 | } 218 | out := new(TestRunStatus) 219 | x.DeepCopyInto(out) 220 | return out 221 | } 222 | 223 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 224 | func (in *TestTemplate) DeepCopyInto(out *TestTemplate) { 225 | *out = *in 226 | out.TypeMeta = in.TypeMeta 227 | in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) 228 | in.Spec.DeepCopyInto(&out.Spec) 229 | return 230 | } 231 | 232 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, creating a new TestTemplate. 233 | func (x *TestTemplate) DeepCopy() *TestTemplate { 234 | if x == nil { 235 | return nil 236 | } 237 | out := new(TestTemplate) 238 | x.DeepCopyInto(out) 239 | return out 240 | } 241 | 242 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 243 | func (x *TestTemplate) DeepCopyObject() runtime.Object { 244 | if c := x.DeepCopy(); c != nil { 245 | return c 246 | } else { 247 | return nil 248 | } 249 | } 250 | 251 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 252 | func (in *TestTemplateList) DeepCopyInto(out *TestTemplateList) { 253 | *out = *in 254 | out.TypeMeta = in.TypeMeta 255 | out.ListMeta = in.ListMeta 256 | if in.Items != nil { 257 | in, out := &in.Items, &out.Items 258 | *out = make([]TestTemplate, len(*in)) 259 | for i := range *in { 260 | (*in)[i].DeepCopyInto(&(*out)[i]) 261 | } 262 | } 263 | return 264 | } 265 | 266 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, creating a new TestTemplateList. 267 | func (x *TestTemplateList) DeepCopy() *TestTemplateList { 268 | if x == nil { 269 | return nil 270 | } 271 | out := new(TestTemplateList) 272 | x.DeepCopyInto(out) 273 | return out 274 | } 275 | 276 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 277 | func (x *TestTemplateList) DeepCopyObject() runtime.Object { 278 | if c := x.DeepCopy(); c != nil { 279 | return c 280 | } else { 281 | return nil 282 | } 283 | } 284 | 285 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 286 | func (in *TestTemplateSpec) DeepCopyInto(out *TestTemplateSpec) { 287 | *out = *in 288 | in.Template.DeepCopyInto(&out.Template) 289 | return 290 | } 291 | 292 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, creating a new TestTemplateSpec. 293 | func (x *TestTemplateSpec) DeepCopy() *TestTemplateSpec { 294 | if x == nil { 295 | return nil 296 | } 297 | out := new(TestTemplateSpec) 298 | x.DeepCopyInto(out) 299 | return out 300 | } 301 | -------------------------------------------------------------------------------- /pkg/apis/tester/v1alpha1/zz_generated.defaults.go: -------------------------------------------------------------------------------- 1 | // +build !ignore_autogenerated 2 | 3 | /* 4 | 5 | MIT License 6 | 7 | Copyright (c) 2017 Sean Ross-Ross 8 | 9 | See License in the root of this repo. 10 | 11 | */ 12 | 13 | // This file was autogenerated by defaulter-gen. Do not edit it manually! 14 | 15 | package v1alpha1 16 | 17 | import ( 18 | runtime "k8s.io/apimachinery/pkg/runtime" 19 | ) 20 | 21 | // RegisterDefaults adds defaulters functions to the given scheme. 22 | // Public to allow building arbitrary schemes. 23 | // All generated defaulters are covering - they call all nested defaulters. 24 | func RegisterDefaults(scheme *runtime.Scheme) error { 25 | return nil 26 | } 27 | -------------------------------------------------------------------------------- /pkg/apis/tester/zz_generated.deepcopy.go: -------------------------------------------------------------------------------- 1 | // +build !ignore_autogenerated 2 | 3 | /* 4 | 5 | MIT License 6 | 7 | Copyright (c) 2017 Sean Ross-Ross 8 | 9 | See License in the root of this repo. 10 | 11 | */ 12 | 13 | // This file was autogenerated by deepcopy-gen. Do not edit it manually! 14 | 15 | package tester 16 | 17 | import ( 18 | v1 "k8s.io/api/core/v1" 19 | meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 20 | conversion "k8s.io/apimachinery/pkg/conversion" 21 | runtime "k8s.io/apimachinery/pkg/runtime" 22 | reflect "reflect" 23 | ) 24 | 25 | // Deprecated: register deep-copy functions. 26 | func init() { 27 | SchemeBuilder.Register(RegisterDeepCopies) 28 | } 29 | 30 | // Deprecated: RegisterDeepCopies adds deep-copy functions to the given scheme. Public 31 | // to allow building arbitrary schemes. 32 | func RegisterDeepCopies(scheme *runtime.Scheme) error { 33 | return scheme.AddGeneratedDeepCopyFuncs( 34 | conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error { 35 | in.(*TestRun).DeepCopyInto(out.(*TestRun)) 36 | return nil 37 | }, InType: reflect.TypeOf(&TestRun{})}, 38 | conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error { 39 | in.(*TestRunList).DeepCopyInto(out.(*TestRunList)) 40 | return nil 41 | }, InType: reflect.TypeOf(&TestRunList{})}, 42 | conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error { 43 | in.(*TestRunRecord).DeepCopyInto(out.(*TestRunRecord)) 44 | return nil 45 | }, InType: reflect.TypeOf(&TestRunRecord{})}, 46 | conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error { 47 | in.(*TestRunSpec).DeepCopyInto(out.(*TestRunSpec)) 48 | return nil 49 | }, InType: reflect.TypeOf(&TestRunSpec{})}, 50 | conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error { 51 | in.(*TestRunStatus).DeepCopyInto(out.(*TestRunStatus)) 52 | return nil 53 | }, InType: reflect.TypeOf(&TestRunStatus{})}, 54 | conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error { 55 | in.(*TestTemplate).DeepCopyInto(out.(*TestTemplate)) 56 | return nil 57 | }, InType: reflect.TypeOf(&TestTemplate{})}, 58 | conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error { 59 | in.(*TestTemplateList).DeepCopyInto(out.(*TestTemplateList)) 60 | return nil 61 | }, InType: reflect.TypeOf(&TestTemplateList{})}, 62 | conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error { 63 | in.(*TestTemplateSpec).DeepCopyInto(out.(*TestTemplateSpec)) 64 | return nil 65 | }, InType: reflect.TypeOf(&TestTemplateSpec{})}, 66 | ) 67 | } 68 | 69 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 70 | func (in *TestRun) DeepCopyInto(out *TestRun) { 71 | *out = *in 72 | out.TypeMeta = in.TypeMeta 73 | in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) 74 | in.Spec.DeepCopyInto(&out.Spec) 75 | in.Status.DeepCopyInto(&out.Status) 76 | return 77 | } 78 | 79 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, creating a new TestRun. 80 | func (x *TestRun) DeepCopy() *TestRun { 81 | if x == nil { 82 | return nil 83 | } 84 | out := new(TestRun) 85 | x.DeepCopyInto(out) 86 | return out 87 | } 88 | 89 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 90 | func (x *TestRun) DeepCopyObject() runtime.Object { 91 | if c := x.DeepCopy(); c != nil { 92 | return c 93 | } else { 94 | return nil 95 | } 96 | } 97 | 98 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 99 | func (in *TestRunList) DeepCopyInto(out *TestRunList) { 100 | *out = *in 101 | out.TypeMeta = in.TypeMeta 102 | out.ListMeta = in.ListMeta 103 | if in.Items != nil { 104 | in, out := &in.Items, &out.Items 105 | *out = make([]TestRun, len(*in)) 106 | for i := range *in { 107 | (*in)[i].DeepCopyInto(&(*out)[i]) 108 | } 109 | } 110 | return 111 | } 112 | 113 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, creating a new TestRunList. 114 | func (x *TestRunList) DeepCopy() *TestRunList { 115 | if x == nil { 116 | return nil 117 | } 118 | out := new(TestRunList) 119 | x.DeepCopyInto(out) 120 | return out 121 | } 122 | 123 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 124 | func (x *TestRunList) DeepCopyObject() runtime.Object { 125 | if c := x.DeepCopy(); c != nil { 126 | return c 127 | } else { 128 | return nil 129 | } 130 | } 131 | 132 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 133 | func (in *TestRunRecord) DeepCopyInto(out *TestRunRecord) { 134 | *out = *in 135 | if in.PodRef != nil { 136 | in, out := &in.PodRef, &out.PodRef 137 | if *in == nil { 138 | *out = nil 139 | } else { 140 | *out = new(v1.ObjectReference) 141 | **out = **in 142 | } 143 | } 144 | if in.StartTime != nil { 145 | in, out := &in.StartTime, &out.StartTime 146 | if *in == nil { 147 | *out = nil 148 | } else { 149 | *out = new(meta_v1.Time) 150 | (*in).DeepCopyInto(*out) 151 | } 152 | } 153 | if in.EndTime != nil { 154 | in, out := &in.EndTime, &out.EndTime 155 | if *in == nil { 156 | *out = nil 157 | } else { 158 | *out = new(meta_v1.Time) 159 | (*in).DeepCopyInto(*out) 160 | } 161 | } 162 | return 163 | } 164 | 165 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, creating a new TestRunRecord. 166 | func (x *TestRunRecord) DeepCopy() *TestRunRecord { 167 | if x == nil { 168 | return nil 169 | } 170 | out := new(TestRunRecord) 171 | x.DeepCopyInto(out) 172 | return out 173 | } 174 | 175 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 176 | func (in *TestRunSpec) DeepCopyInto(out *TestRunSpec) { 177 | *out = *in 178 | if in.Selector != nil { 179 | in, out := &in.Selector, &out.Selector 180 | if *in == nil { 181 | *out = nil 182 | } else { 183 | *out = new(meta_v1.LabelSelector) 184 | (*in).DeepCopyInto(*out) 185 | } 186 | } 187 | return 188 | } 189 | 190 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, creating a new TestRunSpec. 191 | func (x *TestRunSpec) DeepCopy() *TestRunSpec { 192 | if x == nil { 193 | return nil 194 | } 195 | out := new(TestRunSpec) 196 | x.DeepCopyInto(out) 197 | return out 198 | } 199 | 200 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 201 | func (in *TestRunStatus) DeepCopyInto(out *TestRunStatus) { 202 | *out = *in 203 | if in.Records != nil { 204 | in, out := &in.Records, &out.Records 205 | *out = make([]TestRunRecord, len(*in)) 206 | for i := range *in { 207 | (*in)[i].DeepCopyInto(&(*out)[i]) 208 | } 209 | } 210 | return 211 | } 212 | 213 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, creating a new TestRunStatus. 214 | func (x *TestRunStatus) DeepCopy() *TestRunStatus { 215 | if x == nil { 216 | return nil 217 | } 218 | out := new(TestRunStatus) 219 | x.DeepCopyInto(out) 220 | return out 221 | } 222 | 223 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 224 | func (in *TestTemplate) DeepCopyInto(out *TestTemplate) { 225 | *out = *in 226 | out.TypeMeta = in.TypeMeta 227 | in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) 228 | in.Spec.DeepCopyInto(&out.Spec) 229 | return 230 | } 231 | 232 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, creating a new TestTemplate. 233 | func (x *TestTemplate) DeepCopy() *TestTemplate { 234 | if x == nil { 235 | return nil 236 | } 237 | out := new(TestTemplate) 238 | x.DeepCopyInto(out) 239 | return out 240 | } 241 | 242 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 243 | func (x *TestTemplate) DeepCopyObject() runtime.Object { 244 | if c := x.DeepCopy(); c != nil { 245 | return c 246 | } else { 247 | return nil 248 | } 249 | } 250 | 251 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 252 | func (in *TestTemplateList) DeepCopyInto(out *TestTemplateList) { 253 | *out = *in 254 | out.TypeMeta = in.TypeMeta 255 | out.ListMeta = in.ListMeta 256 | if in.Items != nil { 257 | in, out := &in.Items, &out.Items 258 | *out = make([]TestTemplate, len(*in)) 259 | for i := range *in { 260 | (*in)[i].DeepCopyInto(&(*out)[i]) 261 | } 262 | } 263 | return 264 | } 265 | 266 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, creating a new TestTemplateList. 267 | func (x *TestTemplateList) DeepCopy() *TestTemplateList { 268 | if x == nil { 269 | return nil 270 | } 271 | out := new(TestTemplateList) 272 | x.DeepCopyInto(out) 273 | return out 274 | } 275 | 276 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 277 | func (x *TestTemplateList) DeepCopyObject() runtime.Object { 278 | if c := x.DeepCopy(); c != nil { 279 | return c 280 | } else { 281 | return nil 282 | } 283 | } 284 | 285 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 286 | func (in *TestTemplateSpec) DeepCopyInto(out *TestTemplateSpec) { 287 | *out = *in 288 | in.Template.DeepCopyInto(&out.Template) 289 | return 290 | } 291 | 292 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, creating a new TestTemplateSpec. 293 | func (x *TestTemplateSpec) DeepCopy() *TestTemplateSpec { 294 | if x == nil { 295 | return nil 296 | } 297 | out := new(TestTemplateSpec) 298 | x.DeepCopyInto(out) 299 | return out 300 | } 301 | -------------------------------------------------------------------------------- /pkg/apis/tester/zz_generated.defaults.go: -------------------------------------------------------------------------------- 1 | // +build !ignore_autogenerated 2 | 3 | /* 4 | 5 | MIT License 6 | 7 | Copyright (c) 2017 Sean Ross-Ross 8 | 9 | See License in the root of this repo. 10 | 11 | */ 12 | 13 | // This file was autogenerated by defaulter-gen. Do not edit it manually! 14 | 15 | package tester 16 | 17 | import ( 18 | runtime "k8s.io/apimachinery/pkg/runtime" 19 | ) 20 | 21 | // RegisterDefaults adds defaulters functions to the given scheme. 22 | // Public to allow building arbitrary schemes. 23 | // All generated defaulters are covering - they call all nested defaulters. 24 | func RegisterDefaults(scheme *runtime.Scheme) error { 25 | return nil 26 | } 27 | -------------------------------------------------------------------------------- /pkg/client/clientset.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Sean Ross-Ross 6 | 7 | See License in the root of this repo. 8 | 9 | */ 10 | package client 11 | 12 | import ( 13 | glog "github.com/golang/glog" 14 | srossrossv1alpha1 "github.com/srossross/k8s-test-controller/pkg/client/typed/srossross/v1alpha1" 15 | discovery "k8s.io/client-go/discovery" 16 | rest "k8s.io/client-go/rest" 17 | flowcontrol "k8s.io/client-go/util/flowcontrol" 18 | ) 19 | 20 | type Interface interface { 21 | Discovery() discovery.DiscoveryInterface 22 | SrossrossV1alpha1() srossrossv1alpha1.SrossrossV1alpha1Interface 23 | // Deprecated: please explicitly pick a version if possible. 24 | Srossross() srossrossv1alpha1.SrossrossV1alpha1Interface 25 | } 26 | 27 | // Clientset contains the clients for groups. Each group has exactly one 28 | // version included in a Clientset. 29 | type Clientset struct { 30 | *discovery.DiscoveryClient 31 | *srossrossv1alpha1.SrossrossV1alpha1Client 32 | } 33 | 34 | // SrossrossV1alpha1 retrieves the SrossrossV1alpha1Client 35 | func (c *Clientset) SrossrossV1alpha1() srossrossv1alpha1.SrossrossV1alpha1Interface { 36 | if c == nil { 37 | return nil 38 | } 39 | return c.SrossrossV1alpha1Client 40 | } 41 | 42 | // Deprecated: Srossross retrieves the default version of SrossrossClient. 43 | // Please explicitly pick a version. 44 | func (c *Clientset) Srossross() srossrossv1alpha1.SrossrossV1alpha1Interface { 45 | if c == nil { 46 | return nil 47 | } 48 | return c.SrossrossV1alpha1Client 49 | } 50 | 51 | // Discovery retrieves the DiscoveryClient 52 | func (c *Clientset) Discovery() discovery.DiscoveryInterface { 53 | if c == nil { 54 | return nil 55 | } 56 | return c.DiscoveryClient 57 | } 58 | 59 | // NewForConfig creates a new Clientset for the given config. 60 | func NewForConfig(c *rest.Config) (*Clientset, error) { 61 | configShallowCopy := *c 62 | if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 { 63 | configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst) 64 | } 65 | var cs Clientset 66 | var err error 67 | cs.SrossrossV1alpha1Client, err = srossrossv1alpha1.NewForConfig(&configShallowCopy) 68 | if err != nil { 69 | return nil, err 70 | } 71 | 72 | cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy) 73 | if err != nil { 74 | glog.Errorf("failed to create the DiscoveryClient: %v", err) 75 | return nil, err 76 | } 77 | return &cs, nil 78 | } 79 | 80 | // NewForConfigOrDie creates a new Clientset for the given config and 81 | // panics if there is an error in the config. 82 | func NewForConfigOrDie(c *rest.Config) *Clientset { 83 | var cs Clientset 84 | cs.SrossrossV1alpha1Client = srossrossv1alpha1.NewForConfigOrDie(c) 85 | 86 | cs.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c) 87 | return &cs 88 | } 89 | 90 | // New creates a new Clientset for the given RESTClient. 91 | func New(c rest.Interface) *Clientset { 92 | var cs Clientset 93 | cs.SrossrossV1alpha1Client = srossrossv1alpha1.New(c) 94 | 95 | cs.DiscoveryClient = discovery.NewDiscoveryClient(c) 96 | return &cs 97 | } 98 | -------------------------------------------------------------------------------- /pkg/client/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Sean Ross-Ross 6 | 7 | See License in the root of this repo. 8 | 9 | */ 10 | 11 | // This package is generated by client-gen with custom arguments. 12 | 13 | // This package has the automatically generated clientset. 14 | package client 15 | -------------------------------------------------------------------------------- /pkg/client/fake/clientset_generated.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Sean Ross-Ross 6 | 7 | See License in the root of this repo. 8 | 9 | */ 10 | package fake 11 | 12 | import ( 13 | clientset "github.com/srossross/k8s-test-controller/pkg/client" 14 | srossrossv1alpha1 "github.com/srossross/k8s-test-controller/pkg/client/typed/srossross/v1alpha1" 15 | fakesrossrossv1alpha1 "github.com/srossross/k8s-test-controller/pkg/client/typed/srossross/v1alpha1/fake" 16 | "k8s.io/apimachinery/pkg/runtime" 17 | "k8s.io/apimachinery/pkg/watch" 18 | "k8s.io/client-go/discovery" 19 | fakediscovery "k8s.io/client-go/discovery/fake" 20 | "k8s.io/client-go/testing" 21 | ) 22 | 23 | // NewSimpleClientset returns a clientset that will respond with the provided objects. 24 | // It's backed by a very simple object tracker that processes creates, updates and deletions as-is, 25 | // without applying any validations and/or defaults. It shouldn't be considered a replacement 26 | // for a real clientset and is mostly useful in simple unit tests. 27 | func NewSimpleClientset(objects ...runtime.Object) *Clientset { 28 | o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder()) 29 | for _, obj := range objects { 30 | if err := o.Add(obj); err != nil { 31 | panic(err) 32 | } 33 | } 34 | 35 | fakePtr := testing.Fake{} 36 | fakePtr.AddReactor("*", "*", testing.ObjectReaction(o)) 37 | 38 | fakePtr.AddWatchReactor("*", testing.DefaultWatchReactor(watch.NewFake(), nil)) 39 | 40 | return &Clientset{fakePtr} 41 | } 42 | 43 | // Clientset implements clientset.Interface. Meant to be embedded into a 44 | // struct to get a default implementation. This makes faking out just the method 45 | // you want to test easier. 46 | type Clientset struct { 47 | testing.Fake 48 | } 49 | 50 | func (c *Clientset) Discovery() discovery.DiscoveryInterface { 51 | return &fakediscovery.FakeDiscovery{Fake: &c.Fake} 52 | } 53 | 54 | var _ clientset.Interface = &Clientset{} 55 | 56 | // SrossrossV1alpha1 retrieves the SrossrossV1alpha1Client 57 | func (c *Clientset) SrossrossV1alpha1() srossrossv1alpha1.SrossrossV1alpha1Interface { 58 | return &fakesrossrossv1alpha1.FakeSrossrossV1alpha1{Fake: &c.Fake} 59 | } 60 | 61 | // Srossross retrieves the SrossrossV1alpha1Client 62 | func (c *Clientset) Srossross() srossrossv1alpha1.SrossrossV1alpha1Interface { 63 | return &fakesrossrossv1alpha1.FakeSrossrossV1alpha1{Fake: &c.Fake} 64 | } 65 | -------------------------------------------------------------------------------- /pkg/client/fake/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Sean Ross-Ross 6 | 7 | See License in the root of this repo. 8 | 9 | */ 10 | 11 | // This package is generated by client-gen with custom arguments. 12 | 13 | // This package has the automatically generated fake clientset. 14 | package fake 15 | -------------------------------------------------------------------------------- /pkg/client/fake/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Sean Ross-Ross 6 | 7 | See License in the root of this repo. 8 | 9 | */ 10 | package fake 11 | 12 | import ( 13 | srossrossv1alpha1 "github.com/srossross/k8s-test-controller/pkg/apis/tester/v1alpha1" 14 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 15 | runtime "k8s.io/apimachinery/pkg/runtime" 16 | schema "k8s.io/apimachinery/pkg/runtime/schema" 17 | serializer "k8s.io/apimachinery/pkg/runtime/serializer" 18 | ) 19 | 20 | var scheme = runtime.NewScheme() 21 | var codecs = serializer.NewCodecFactory(scheme) 22 | var parameterCodec = runtime.NewParameterCodec(scheme) 23 | 24 | func init() { 25 | v1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"}) 26 | AddToScheme(scheme) 27 | } 28 | 29 | // AddToScheme adds all types of this clientset into the given scheme. This allows composition 30 | // of clientsets, like in: 31 | // 32 | // import ( 33 | // "k8s.io/client-go/kubernetes" 34 | // clientsetscheme "k8s.io/client-go/kuberentes/scheme" 35 | // aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" 36 | // ) 37 | // 38 | // kclientset, _ := kubernetes.NewForConfig(c) 39 | // aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) 40 | // 41 | // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types 42 | // correctly. 43 | func AddToScheme(scheme *runtime.Scheme) { 44 | srossrossv1alpha1.AddToScheme(scheme) 45 | 46 | } 47 | -------------------------------------------------------------------------------- /pkg/client/internalclientset/clientset.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Sean Ross-Ross 6 | 7 | See License in the root of this repo. 8 | 9 | */ 10 | package internalclientset 11 | 12 | import ( 13 | glog "github.com/golang/glog" 14 | testerinternalversion "github.com/srossross/k8s-test-controller/pkg/client/internalclientset/typed/tester/internalversion" 15 | discovery "k8s.io/client-go/discovery" 16 | rest "k8s.io/client-go/rest" 17 | flowcontrol "k8s.io/client-go/util/flowcontrol" 18 | ) 19 | 20 | type Interface interface { 21 | Discovery() discovery.DiscoveryInterface 22 | Tester() testerinternalversion.TesterInterface 23 | } 24 | 25 | // Clientset contains the clients for groups. Each group has exactly one 26 | // version included in a Clientset. 27 | type Clientset struct { 28 | *discovery.DiscoveryClient 29 | *testerinternalversion.TesterClient 30 | } 31 | 32 | // Tester retrieves the TesterClient 33 | func (c *Clientset) Tester() testerinternalversion.TesterInterface { 34 | if c == nil { 35 | return nil 36 | } 37 | return c.TesterClient 38 | } 39 | 40 | // Discovery retrieves the DiscoveryClient 41 | func (c *Clientset) Discovery() discovery.DiscoveryInterface { 42 | if c == nil { 43 | return nil 44 | } 45 | return c.DiscoveryClient 46 | } 47 | 48 | // NewForConfig creates a new Clientset for the given config. 49 | func NewForConfig(c *rest.Config) (*Clientset, error) { 50 | configShallowCopy := *c 51 | if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 { 52 | configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst) 53 | } 54 | var cs Clientset 55 | var err error 56 | cs.TesterClient, err = testerinternalversion.NewForConfig(&configShallowCopy) 57 | if err != nil { 58 | return nil, err 59 | } 60 | 61 | cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy) 62 | if err != nil { 63 | glog.Errorf("failed to create the DiscoveryClient: %v", err) 64 | return nil, err 65 | } 66 | return &cs, nil 67 | } 68 | 69 | // NewForConfigOrDie creates a new Clientset for the given config and 70 | // panics if there is an error in the config. 71 | func NewForConfigOrDie(c *rest.Config) *Clientset { 72 | var cs Clientset 73 | cs.TesterClient = testerinternalversion.NewForConfigOrDie(c) 74 | 75 | cs.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c) 76 | return &cs 77 | } 78 | 79 | // New creates a new Clientset for the given RESTClient. 80 | func New(c rest.Interface) *Clientset { 81 | var cs Clientset 82 | cs.TesterClient = testerinternalversion.New(c) 83 | 84 | cs.DiscoveryClient = discovery.NewDiscoveryClient(c) 85 | return &cs 86 | } 87 | -------------------------------------------------------------------------------- /pkg/client/internalclientset/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Sean Ross-Ross 6 | 7 | See License in the root of this repo. 8 | 9 | */ 10 | 11 | // This package is generated by client-gen with custom arguments. 12 | 13 | // This package has the automatically generated clientset. 14 | package internalclientset 15 | -------------------------------------------------------------------------------- /pkg/client/internalclientset/fake/clientset_generated.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Sean Ross-Ross 6 | 7 | See License in the root of this repo. 8 | 9 | */ 10 | package fake 11 | 12 | import ( 13 | clientset "github.com/srossross/k8s-test-controller/pkg/client/internalclientset" 14 | testerinternalversion "github.com/srossross/k8s-test-controller/pkg/client/internalclientset/typed/tester/internalversion" 15 | faketesterinternalversion "github.com/srossross/k8s-test-controller/pkg/client/internalclientset/typed/tester/internalversion/fake" 16 | "k8s.io/apimachinery/pkg/runtime" 17 | "k8s.io/apimachinery/pkg/watch" 18 | "k8s.io/client-go/discovery" 19 | fakediscovery "k8s.io/client-go/discovery/fake" 20 | "k8s.io/client-go/testing" 21 | ) 22 | 23 | // NewSimpleClientset returns a clientset that will respond with the provided objects. 24 | // It's backed by a very simple object tracker that processes creates, updates and deletions as-is, 25 | // without applying any validations and/or defaults. It shouldn't be considered a replacement 26 | // for a real clientset and is mostly useful in simple unit tests. 27 | func NewSimpleClientset(objects ...runtime.Object) *Clientset { 28 | o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder()) 29 | for _, obj := range objects { 30 | if err := o.Add(obj); err != nil { 31 | panic(err) 32 | } 33 | } 34 | 35 | fakePtr := testing.Fake{} 36 | fakePtr.AddReactor("*", "*", testing.ObjectReaction(o)) 37 | 38 | fakePtr.AddWatchReactor("*", testing.DefaultWatchReactor(watch.NewFake(), nil)) 39 | 40 | return &Clientset{fakePtr} 41 | } 42 | 43 | // Clientset implements clientset.Interface. Meant to be embedded into a 44 | // struct to get a default implementation. This makes faking out just the method 45 | // you want to test easier. 46 | type Clientset struct { 47 | testing.Fake 48 | } 49 | 50 | func (c *Clientset) Discovery() discovery.DiscoveryInterface { 51 | return &fakediscovery.FakeDiscovery{Fake: &c.Fake} 52 | } 53 | 54 | var _ clientset.Interface = &Clientset{} 55 | 56 | // Tester retrieves the TesterClient 57 | func (c *Clientset) Tester() testerinternalversion.TesterInterface { 58 | return &faketesterinternalversion.FakeTester{Fake: &c.Fake} 59 | } 60 | -------------------------------------------------------------------------------- /pkg/client/internalclientset/fake/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Sean Ross-Ross 6 | 7 | See License in the root of this repo. 8 | 9 | */ 10 | 11 | // This package is generated by client-gen with custom arguments. 12 | 13 | // This package has the automatically generated fake clientset. 14 | package fake 15 | -------------------------------------------------------------------------------- /pkg/client/internalclientset/fake/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Sean Ross-Ross 6 | 7 | See License in the root of this repo. 8 | 9 | */ 10 | package fake 11 | 12 | import ( 13 | testerinternalversion "github.com/srossross/k8s-test-controller/pkg/apis/tester" 14 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 15 | runtime "k8s.io/apimachinery/pkg/runtime" 16 | schema "k8s.io/apimachinery/pkg/runtime/schema" 17 | serializer "k8s.io/apimachinery/pkg/runtime/serializer" 18 | ) 19 | 20 | var scheme = runtime.NewScheme() 21 | var codecs = serializer.NewCodecFactory(scheme) 22 | var parameterCodec = runtime.NewParameterCodec(scheme) 23 | 24 | func init() { 25 | v1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"}) 26 | AddToScheme(scheme) 27 | } 28 | 29 | // AddToScheme adds all types of this clientset into the given scheme. This allows composition 30 | // of clientsets, like in: 31 | // 32 | // import ( 33 | // "k8s.io/client-go/kubernetes" 34 | // clientsetscheme "k8s.io/client-go/kuberentes/scheme" 35 | // aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" 36 | // ) 37 | // 38 | // kclientset, _ := kubernetes.NewForConfig(c) 39 | // aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) 40 | // 41 | // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types 42 | // correctly. 43 | func AddToScheme(scheme *runtime.Scheme) { 44 | testerinternalversion.AddToScheme(scheme) 45 | 46 | } 47 | -------------------------------------------------------------------------------- /pkg/client/internalclientset/scheme/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Sean Ross-Ross 6 | 7 | See License in the root of this repo. 8 | 9 | */ 10 | 11 | // This package is generated by client-gen with custom arguments. 12 | 13 | // This package contains the scheme of the automatically generated clientset. 14 | package scheme 15 | -------------------------------------------------------------------------------- /pkg/client/internalclientset/scheme/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Sean Ross-Ross 6 | 7 | See License in the root of this repo. 8 | 9 | */ 10 | package scheme 11 | 12 | import ( 13 | tester "github.com/srossross/k8s-test-controller/pkg/apis/tester/install" 14 | announced "k8s.io/apimachinery/pkg/apimachinery/announced" 15 | registered "k8s.io/apimachinery/pkg/apimachinery/registered" 16 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 17 | runtime "k8s.io/apimachinery/pkg/runtime" 18 | schema "k8s.io/apimachinery/pkg/runtime/schema" 19 | serializer "k8s.io/apimachinery/pkg/runtime/serializer" 20 | os "os" 21 | ) 22 | 23 | var Scheme = runtime.NewScheme() 24 | var Codecs = serializer.NewCodecFactory(Scheme) 25 | var ParameterCodec = runtime.NewParameterCodec(Scheme) 26 | 27 | var Registry = registered.NewOrDie(os.Getenv("KUBE_API_VERSIONS")) 28 | var GroupFactoryRegistry = make(announced.APIGroupFactoryRegistry) 29 | 30 | func init() { 31 | v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"}) 32 | Install(GroupFactoryRegistry, Registry, Scheme) 33 | } 34 | 35 | // Install registers the API group and adds types to a scheme 36 | func Install(groupFactoryRegistry announced.APIGroupFactoryRegistry, registry *registered.APIRegistrationManager, scheme *runtime.Scheme) { 37 | tester.Install(groupFactoryRegistry, registry, scheme) 38 | 39 | } 40 | -------------------------------------------------------------------------------- /pkg/client/internalclientset/typed/tester/internalversion/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Sean Ross-Ross 6 | 7 | See License in the root of this repo. 8 | 9 | */ 10 | 11 | // This package is generated by client-gen with custom arguments. 12 | 13 | // This package has the automatically generated typed clients. 14 | package internalversion 15 | -------------------------------------------------------------------------------- /pkg/client/internalclientset/typed/tester/internalversion/fake/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Sean Ross-Ross 6 | 7 | See License in the root of this repo. 8 | 9 | */ 10 | 11 | // This package is generated by client-gen with custom arguments. 12 | 13 | // Package fake has the automatically generated clients. 14 | package fake 15 | -------------------------------------------------------------------------------- /pkg/client/internalclientset/typed/tester/internalversion/fake/fake_tester_client.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Sean Ross-Ross 6 | 7 | See License in the root of this repo. 8 | 9 | */ 10 | package fake 11 | 12 | import ( 13 | internalversion "github.com/srossross/k8s-test-controller/pkg/client/internalclientset/typed/tester/internalversion" 14 | ) 15 | -------------------------------------------------------------------------------- /pkg/client/internalclientset/typed/tester/internalversion/generated_expansion.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Sean Ross-Ross 6 | 7 | See License in the root of this repo. 8 | 9 | */ 10 | package internalversion 11 | -------------------------------------------------------------------------------- /pkg/client/internalclientset/typed/tester/internalversion/tester_client.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Sean Ross-Ross 6 | 7 | See License in the root of this repo. 8 | 9 | */ 10 | package internalversion 11 | 12 | import ( 13 | "github.com/srossross/k8s-test-controller/pkg/client/internalclientset/scheme" 14 | ) 15 | -------------------------------------------------------------------------------- /pkg/client/scheme/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Sean Ross-Ross 6 | 7 | See License in the root of this repo. 8 | 9 | */ 10 | 11 | // This package is generated by client-gen with custom arguments. 12 | 13 | // This package contains the scheme of the automatically generated clientset. 14 | package scheme 15 | -------------------------------------------------------------------------------- /pkg/client/scheme/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Sean Ross-Ross 6 | 7 | See License in the root of this repo. 8 | 9 | */ 10 | package scheme 11 | 12 | import ( 13 | srossrossv1alpha1 "github.com/srossross/k8s-test-controller/pkg/apis/tester/v1alpha1" 14 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 15 | runtime "k8s.io/apimachinery/pkg/runtime" 16 | schema "k8s.io/apimachinery/pkg/runtime/schema" 17 | serializer "k8s.io/apimachinery/pkg/runtime/serializer" 18 | ) 19 | 20 | var Scheme = runtime.NewScheme() 21 | var Codecs = serializer.NewCodecFactory(Scheme) 22 | var ParameterCodec = runtime.NewParameterCodec(Scheme) 23 | 24 | func init() { 25 | v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"}) 26 | AddToScheme(Scheme) 27 | } 28 | 29 | // AddToScheme adds all types of this clientset into the given scheme. This allows composition 30 | // of clientsets, like in: 31 | // 32 | // import ( 33 | // "k8s.io/client-go/kubernetes" 34 | // clientsetscheme "k8s.io/client-go/kuberentes/scheme" 35 | // aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" 36 | // ) 37 | // 38 | // kclientset, _ := kubernetes.NewForConfig(c) 39 | // aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) 40 | // 41 | // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types 42 | // correctly. 43 | func AddToScheme(scheme *runtime.Scheme) { 44 | srossrossv1alpha1.AddToScheme(scheme) 45 | 46 | } 47 | -------------------------------------------------------------------------------- /pkg/client/typed/srossross/v1alpha1/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Sean Ross-Ross 6 | 7 | See License in the root of this repo. 8 | 9 | */ 10 | 11 | // This package is generated by client-gen with custom arguments. 12 | 13 | // This package has the automatically generated typed clients. 14 | package v1alpha1 15 | -------------------------------------------------------------------------------- /pkg/client/typed/srossross/v1alpha1/fake/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Sean Ross-Ross 6 | 7 | See License in the root of this repo. 8 | 9 | */ 10 | 11 | // This package is generated by client-gen with custom arguments. 12 | 13 | // Package fake has the automatically generated clients. 14 | package fake 15 | -------------------------------------------------------------------------------- /pkg/client/typed/srossross/v1alpha1/fake/fake_srossross_client.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Sean Ross-Ross 6 | 7 | See License in the root of this repo. 8 | 9 | */ 10 | package fake 11 | 12 | import ( 13 | v1alpha1 "github.com/srossross/k8s-test-controller/pkg/client/typed/srossross/v1alpha1" 14 | rest "k8s.io/client-go/rest" 15 | testing "k8s.io/client-go/testing" 16 | ) 17 | 18 | type FakeSrossrossV1alpha1 struct { 19 | *testing.Fake 20 | } 21 | 22 | func (c *FakeSrossrossV1alpha1) TestRuns(namespace string) v1alpha1.TestRunInterface { 23 | return &FakeTestRuns{c, namespace} 24 | } 25 | 26 | func (c *FakeSrossrossV1alpha1) TestTemplates(namespace string) v1alpha1.TestTemplateInterface { 27 | return &FakeTestTemplates{c, namespace} 28 | } 29 | 30 | // RESTClient returns a RESTClient that is used to communicate 31 | // with API server by this client implementation. 32 | func (c *FakeSrossrossV1alpha1) RESTClient() rest.Interface { 33 | var ret *rest.RESTClient 34 | return ret 35 | } 36 | -------------------------------------------------------------------------------- /pkg/client/typed/srossross/v1alpha1/fake/fake_testrun.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Sean Ross-Ross 6 | 7 | See License in the root of this repo. 8 | 9 | */ 10 | package fake 11 | 12 | import ( 13 | v1alpha1 "github.com/srossross/k8s-test-controller/pkg/apis/tester/v1alpha1" 14 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 15 | labels "k8s.io/apimachinery/pkg/labels" 16 | schema "k8s.io/apimachinery/pkg/runtime/schema" 17 | types "k8s.io/apimachinery/pkg/types" 18 | watch "k8s.io/apimachinery/pkg/watch" 19 | testing "k8s.io/client-go/testing" 20 | ) 21 | 22 | // FakeTestRuns implements TestRunInterface 23 | type FakeTestRuns struct { 24 | Fake *FakeSrossrossV1alpha1 25 | ns string 26 | } 27 | 28 | var testrunsResource = schema.GroupVersionResource{Group: "srossross.github.io", Version: "v1alpha1", Resource: "testruns"} 29 | 30 | var testrunsKind = schema.GroupVersionKind{Group: "srossross.github.io", Version: "v1alpha1", Kind: "TestRun"} 31 | 32 | func (c *FakeTestRuns) Create(testRun *v1alpha1.TestRun) (result *v1alpha1.TestRun, err error) { 33 | obj, err := c.Fake. 34 | Invokes(testing.NewCreateAction(testrunsResource, c.ns, testRun), &v1alpha1.TestRun{}) 35 | 36 | if obj == nil { 37 | return nil, err 38 | } 39 | return obj.(*v1alpha1.TestRun), err 40 | } 41 | 42 | func (c *FakeTestRuns) Update(testRun *v1alpha1.TestRun) (result *v1alpha1.TestRun, err error) { 43 | obj, err := c.Fake. 44 | Invokes(testing.NewUpdateAction(testrunsResource, c.ns, testRun), &v1alpha1.TestRun{}) 45 | 46 | if obj == nil { 47 | return nil, err 48 | } 49 | return obj.(*v1alpha1.TestRun), err 50 | } 51 | 52 | func (c *FakeTestRuns) UpdateStatus(testRun *v1alpha1.TestRun) (*v1alpha1.TestRun, error) { 53 | obj, err := c.Fake. 54 | Invokes(testing.NewUpdateSubresourceAction(testrunsResource, "status", c.ns, testRun), &v1alpha1.TestRun{}) 55 | 56 | if obj == nil { 57 | return nil, err 58 | } 59 | return obj.(*v1alpha1.TestRun), err 60 | } 61 | 62 | func (c *FakeTestRuns) Delete(name string, options *v1.DeleteOptions) error { 63 | _, err := c.Fake. 64 | Invokes(testing.NewDeleteAction(testrunsResource, c.ns, name), &v1alpha1.TestRun{}) 65 | 66 | return err 67 | } 68 | 69 | func (c *FakeTestRuns) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { 70 | action := testing.NewDeleteCollectionAction(testrunsResource, c.ns, listOptions) 71 | 72 | _, err := c.Fake.Invokes(action, &v1alpha1.TestRunList{}) 73 | return err 74 | } 75 | 76 | func (c *FakeTestRuns) Get(name string, options v1.GetOptions) (result *v1alpha1.TestRun, err error) { 77 | obj, err := c.Fake. 78 | Invokes(testing.NewGetAction(testrunsResource, c.ns, name), &v1alpha1.TestRun{}) 79 | 80 | if obj == nil { 81 | return nil, err 82 | } 83 | return obj.(*v1alpha1.TestRun), err 84 | } 85 | 86 | func (c *FakeTestRuns) List(opts v1.ListOptions) (result *v1alpha1.TestRunList, err error) { 87 | obj, err := c.Fake. 88 | Invokes(testing.NewListAction(testrunsResource, testrunsKind, c.ns, opts), &v1alpha1.TestRunList{}) 89 | 90 | if obj == nil { 91 | return nil, err 92 | } 93 | 94 | label, _, _ := testing.ExtractFromListOptions(opts) 95 | if label == nil { 96 | label = labels.Everything() 97 | } 98 | list := &v1alpha1.TestRunList{} 99 | for _, item := range obj.(*v1alpha1.TestRunList).Items { 100 | if label.Matches(labels.Set(item.Labels)) { 101 | list.Items = append(list.Items, item) 102 | } 103 | } 104 | return list, err 105 | } 106 | 107 | // Watch returns a watch.Interface that watches the requested testRuns. 108 | func (c *FakeTestRuns) Watch(opts v1.ListOptions) (watch.Interface, error) { 109 | return c.Fake. 110 | InvokesWatch(testing.NewWatchAction(testrunsResource, c.ns, opts)) 111 | 112 | } 113 | 114 | // Patch applies the patch and returns the patched testRun. 115 | func (c *FakeTestRuns) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.TestRun, err error) { 116 | obj, err := c.Fake. 117 | Invokes(testing.NewPatchSubresourceAction(testrunsResource, c.ns, name, data, subresources...), &v1alpha1.TestRun{}) 118 | 119 | if obj == nil { 120 | return nil, err 121 | } 122 | return obj.(*v1alpha1.TestRun), err 123 | } 124 | -------------------------------------------------------------------------------- /pkg/client/typed/srossross/v1alpha1/fake/fake_testtemplate.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Sean Ross-Ross 6 | 7 | See License in the root of this repo. 8 | 9 | */ 10 | package fake 11 | 12 | import ( 13 | v1alpha1 "github.com/srossross/k8s-test-controller/pkg/apis/tester/v1alpha1" 14 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 15 | labels "k8s.io/apimachinery/pkg/labels" 16 | schema "k8s.io/apimachinery/pkg/runtime/schema" 17 | types "k8s.io/apimachinery/pkg/types" 18 | watch "k8s.io/apimachinery/pkg/watch" 19 | testing "k8s.io/client-go/testing" 20 | ) 21 | 22 | // FakeTestTemplates implements TestTemplateInterface 23 | type FakeTestTemplates struct { 24 | Fake *FakeSrossrossV1alpha1 25 | ns string 26 | } 27 | 28 | var testtemplatesResource = schema.GroupVersionResource{Group: "srossross.github.io", Version: "v1alpha1", Resource: "testtemplates"} 29 | 30 | var testtemplatesKind = schema.GroupVersionKind{Group: "srossross.github.io", Version: "v1alpha1", Kind: "TestTemplate"} 31 | 32 | func (c *FakeTestTemplates) Create(testTemplate *v1alpha1.TestTemplate) (result *v1alpha1.TestTemplate, err error) { 33 | obj, err := c.Fake. 34 | Invokes(testing.NewCreateAction(testtemplatesResource, c.ns, testTemplate), &v1alpha1.TestTemplate{}) 35 | 36 | if obj == nil { 37 | return nil, err 38 | } 39 | return obj.(*v1alpha1.TestTemplate), err 40 | } 41 | 42 | func (c *FakeTestTemplates) Update(testTemplate *v1alpha1.TestTemplate) (result *v1alpha1.TestTemplate, err error) { 43 | obj, err := c.Fake. 44 | Invokes(testing.NewUpdateAction(testtemplatesResource, c.ns, testTemplate), &v1alpha1.TestTemplate{}) 45 | 46 | if obj == nil { 47 | return nil, err 48 | } 49 | return obj.(*v1alpha1.TestTemplate), err 50 | } 51 | 52 | func (c *FakeTestTemplates) Delete(name string, options *v1.DeleteOptions) error { 53 | _, err := c.Fake. 54 | Invokes(testing.NewDeleteAction(testtemplatesResource, c.ns, name), &v1alpha1.TestTemplate{}) 55 | 56 | return err 57 | } 58 | 59 | func (c *FakeTestTemplates) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { 60 | action := testing.NewDeleteCollectionAction(testtemplatesResource, c.ns, listOptions) 61 | 62 | _, err := c.Fake.Invokes(action, &v1alpha1.TestTemplateList{}) 63 | return err 64 | } 65 | 66 | func (c *FakeTestTemplates) Get(name string, options v1.GetOptions) (result *v1alpha1.TestTemplate, err error) { 67 | obj, err := c.Fake. 68 | Invokes(testing.NewGetAction(testtemplatesResource, c.ns, name), &v1alpha1.TestTemplate{}) 69 | 70 | if obj == nil { 71 | return nil, err 72 | } 73 | return obj.(*v1alpha1.TestTemplate), err 74 | } 75 | 76 | func (c *FakeTestTemplates) List(opts v1.ListOptions) (result *v1alpha1.TestTemplateList, err error) { 77 | obj, err := c.Fake. 78 | Invokes(testing.NewListAction(testtemplatesResource, testtemplatesKind, c.ns, opts), &v1alpha1.TestTemplateList{}) 79 | 80 | if obj == nil { 81 | return nil, err 82 | } 83 | 84 | label, _, _ := testing.ExtractFromListOptions(opts) 85 | if label == nil { 86 | label = labels.Everything() 87 | } 88 | list := &v1alpha1.TestTemplateList{} 89 | for _, item := range obj.(*v1alpha1.TestTemplateList).Items { 90 | if label.Matches(labels.Set(item.Labels)) { 91 | list.Items = append(list.Items, item) 92 | } 93 | } 94 | return list, err 95 | } 96 | 97 | // Watch returns a watch.Interface that watches the requested testTemplates. 98 | func (c *FakeTestTemplates) Watch(opts v1.ListOptions) (watch.Interface, error) { 99 | return c.Fake. 100 | InvokesWatch(testing.NewWatchAction(testtemplatesResource, c.ns, opts)) 101 | 102 | } 103 | 104 | // Patch applies the patch and returns the patched testTemplate. 105 | func (c *FakeTestTemplates) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.TestTemplate, err error) { 106 | obj, err := c.Fake. 107 | Invokes(testing.NewPatchSubresourceAction(testtemplatesResource, c.ns, name, data, subresources...), &v1alpha1.TestTemplate{}) 108 | 109 | if obj == nil { 110 | return nil, err 111 | } 112 | return obj.(*v1alpha1.TestTemplate), err 113 | } 114 | -------------------------------------------------------------------------------- /pkg/client/typed/srossross/v1alpha1/generated_expansion.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Sean Ross-Ross 6 | 7 | See License in the root of this repo. 8 | 9 | */ 10 | package v1alpha1 11 | 12 | type TestRunExpansion interface{} 13 | 14 | type TestTemplateExpansion interface{} 15 | -------------------------------------------------------------------------------- /pkg/client/typed/srossross/v1alpha1/srossross_client.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Sean Ross-Ross 6 | 7 | See License in the root of this repo. 8 | 9 | */ 10 | package v1alpha1 11 | 12 | import ( 13 | v1alpha1 "github.com/srossross/k8s-test-controller/pkg/apis/tester/v1alpha1" 14 | "github.com/srossross/k8s-test-controller/pkg/client/scheme" 15 | serializer "k8s.io/apimachinery/pkg/runtime/serializer" 16 | rest "k8s.io/client-go/rest" 17 | ) 18 | 19 | type SrossrossV1alpha1Interface interface { 20 | RESTClient() rest.Interface 21 | TestRunsGetter 22 | TestTemplatesGetter 23 | } 24 | 25 | // SrossrossV1alpha1Client is used to interact with features provided by the srossross.github.io group. 26 | type SrossrossV1alpha1Client struct { 27 | restClient rest.Interface 28 | } 29 | 30 | func (c *SrossrossV1alpha1Client) TestRuns(namespace string) TestRunInterface { 31 | return newTestRuns(c, namespace) 32 | } 33 | 34 | func (c *SrossrossV1alpha1Client) TestTemplates(namespace string) TestTemplateInterface { 35 | return newTestTemplates(c, namespace) 36 | } 37 | 38 | // NewForConfig creates a new SrossrossV1alpha1Client for the given config. 39 | func NewForConfig(c *rest.Config) (*SrossrossV1alpha1Client, error) { 40 | config := *c 41 | if err := setConfigDefaults(&config); err != nil { 42 | return nil, err 43 | } 44 | client, err := rest.RESTClientFor(&config) 45 | if err != nil { 46 | return nil, err 47 | } 48 | return &SrossrossV1alpha1Client{client}, nil 49 | } 50 | 51 | // NewForConfigOrDie creates a new SrossrossV1alpha1Client for the given config and 52 | // panics if there is an error in the config. 53 | func NewForConfigOrDie(c *rest.Config) *SrossrossV1alpha1Client { 54 | client, err := NewForConfig(c) 55 | if err != nil { 56 | panic(err) 57 | } 58 | return client 59 | } 60 | 61 | // New creates a new SrossrossV1alpha1Client for the given RESTClient. 62 | func New(c rest.Interface) *SrossrossV1alpha1Client { 63 | return &SrossrossV1alpha1Client{c} 64 | } 65 | 66 | func setConfigDefaults(config *rest.Config) error { 67 | gv := v1alpha1.SchemeGroupVersion 68 | config.GroupVersion = &gv 69 | config.APIPath = "/apis" 70 | config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs} 71 | 72 | if config.UserAgent == "" { 73 | config.UserAgent = rest.DefaultKubernetesUserAgent() 74 | } 75 | 76 | return nil 77 | } 78 | 79 | // RESTClient returns a RESTClient that is used to communicate 80 | // with API server by this client implementation. 81 | func (c *SrossrossV1alpha1Client) RESTClient() rest.Interface { 82 | if c == nil { 83 | return nil 84 | } 85 | return c.restClient 86 | } 87 | -------------------------------------------------------------------------------- /pkg/client/typed/srossross/v1alpha1/testrun.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Sean Ross-Ross 6 | 7 | See License in the root of this repo. 8 | 9 | */ 10 | package v1alpha1 11 | 12 | import ( 13 | v1alpha1 "github.com/srossross/k8s-test-controller/pkg/apis/tester/v1alpha1" 14 | scheme "github.com/srossross/k8s-test-controller/pkg/client/scheme" 15 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 16 | types "k8s.io/apimachinery/pkg/types" 17 | watch "k8s.io/apimachinery/pkg/watch" 18 | rest "k8s.io/client-go/rest" 19 | ) 20 | 21 | // TestRunsGetter has a method to return a TestRunInterface. 22 | // A group's client should implement this interface. 23 | type TestRunsGetter interface { 24 | TestRuns(namespace string) TestRunInterface 25 | } 26 | 27 | // TestRunInterface has methods to work with TestRun resources. 28 | type TestRunInterface interface { 29 | Create(*v1alpha1.TestRun) (*v1alpha1.TestRun, error) 30 | Update(*v1alpha1.TestRun) (*v1alpha1.TestRun, error) 31 | UpdateStatus(*v1alpha1.TestRun) (*v1alpha1.TestRun, error) 32 | Delete(name string, options *v1.DeleteOptions) error 33 | DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error 34 | Get(name string, options v1.GetOptions) (*v1alpha1.TestRun, error) 35 | List(opts v1.ListOptions) (*v1alpha1.TestRunList, error) 36 | Watch(opts v1.ListOptions) (watch.Interface, error) 37 | Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.TestRun, err error) 38 | TestRunExpansion 39 | } 40 | 41 | // testRuns implements TestRunInterface 42 | type testRuns struct { 43 | client rest.Interface 44 | ns string 45 | } 46 | 47 | // newTestRuns returns a TestRuns 48 | func newTestRuns(c *SrossrossV1alpha1Client, namespace string) *testRuns { 49 | return &testRuns{ 50 | client: c.RESTClient(), 51 | ns: namespace, 52 | } 53 | } 54 | 55 | // Create takes the representation of a testRun and creates it. Returns the server's representation of the testRun, and an error, if there is any. 56 | func (c *testRuns) Create(testRun *v1alpha1.TestRun) (result *v1alpha1.TestRun, err error) { 57 | result = &v1alpha1.TestRun{} 58 | err = c.client.Post(). 59 | Namespace(c.ns). 60 | Resource("testruns"). 61 | Body(testRun). 62 | Do(). 63 | Into(result) 64 | return 65 | } 66 | 67 | // Update takes the representation of a testRun and updates it. Returns the server's representation of the testRun, and an error, if there is any. 68 | func (c *testRuns) Update(testRun *v1alpha1.TestRun) (result *v1alpha1.TestRun, err error) { 69 | result = &v1alpha1.TestRun{} 70 | err = c.client.Put(). 71 | Namespace(c.ns). 72 | Resource("testruns"). 73 | Name(testRun.Name). 74 | Body(testRun). 75 | Do(). 76 | Into(result) 77 | return 78 | } 79 | 80 | // UpdateStatus was generated because the type contains a Status member. 81 | // Add a +genclientstatus=false comment above the type to avoid generating UpdateStatus(). 82 | 83 | func (c *testRuns) UpdateStatus(testRun *v1alpha1.TestRun) (result *v1alpha1.TestRun, err error) { 84 | result = &v1alpha1.TestRun{} 85 | err = c.client.Put(). 86 | Namespace(c.ns). 87 | Resource("testruns"). 88 | Name(testRun.Name). 89 | SubResource("status"). 90 | Body(testRun). 91 | Do(). 92 | Into(result) 93 | return 94 | } 95 | 96 | // Delete takes name of the testRun and deletes it. Returns an error if one occurs. 97 | func (c *testRuns) Delete(name string, options *v1.DeleteOptions) error { 98 | return c.client.Delete(). 99 | Namespace(c.ns). 100 | Resource("testruns"). 101 | Name(name). 102 | Body(options). 103 | Do(). 104 | Error() 105 | } 106 | 107 | // DeleteCollection deletes a collection of objects. 108 | func (c *testRuns) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { 109 | return c.client.Delete(). 110 | Namespace(c.ns). 111 | Resource("testruns"). 112 | VersionedParams(&listOptions, scheme.ParameterCodec). 113 | Body(options). 114 | Do(). 115 | Error() 116 | } 117 | 118 | // Get takes name of the testRun, and returns the corresponding testRun object, and an error if there is any. 119 | func (c *testRuns) Get(name string, options v1.GetOptions) (result *v1alpha1.TestRun, err error) { 120 | result = &v1alpha1.TestRun{} 121 | err = c.client.Get(). 122 | Namespace(c.ns). 123 | Resource("testruns"). 124 | Name(name). 125 | VersionedParams(&options, scheme.ParameterCodec). 126 | Do(). 127 | Into(result) 128 | return 129 | } 130 | 131 | // List takes label and field selectors, and returns the list of TestRuns that match those selectors. 132 | func (c *testRuns) List(opts v1.ListOptions) (result *v1alpha1.TestRunList, err error) { 133 | result = &v1alpha1.TestRunList{} 134 | err = c.client.Get(). 135 | Namespace(c.ns). 136 | Resource("testruns"). 137 | VersionedParams(&opts, scheme.ParameterCodec). 138 | Do(). 139 | Into(result) 140 | return 141 | } 142 | 143 | // Watch returns a watch.Interface that watches the requested testRuns. 144 | func (c *testRuns) Watch(opts v1.ListOptions) (watch.Interface, error) { 145 | opts.Watch = true 146 | return c.client.Get(). 147 | Namespace(c.ns). 148 | Resource("testruns"). 149 | VersionedParams(&opts, scheme.ParameterCodec). 150 | Watch() 151 | } 152 | 153 | // Patch applies the patch and returns the patched testRun. 154 | func (c *testRuns) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.TestRun, err error) { 155 | result = &v1alpha1.TestRun{} 156 | err = c.client.Patch(pt). 157 | Namespace(c.ns). 158 | Resource("testruns"). 159 | SubResource(subresources...). 160 | Name(name). 161 | Body(data). 162 | Do(). 163 | Into(result) 164 | return 165 | } 166 | -------------------------------------------------------------------------------- /pkg/client/typed/srossross/v1alpha1/testtemplate.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Sean Ross-Ross 6 | 7 | See License in the root of this repo. 8 | 9 | */ 10 | package v1alpha1 11 | 12 | import ( 13 | v1alpha1 "github.com/srossross/k8s-test-controller/pkg/apis/tester/v1alpha1" 14 | scheme "github.com/srossross/k8s-test-controller/pkg/client/scheme" 15 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 16 | types "k8s.io/apimachinery/pkg/types" 17 | watch "k8s.io/apimachinery/pkg/watch" 18 | rest "k8s.io/client-go/rest" 19 | ) 20 | 21 | // TestTemplatesGetter has a method to return a TestTemplateInterface. 22 | // A group's client should implement this interface. 23 | type TestTemplatesGetter interface { 24 | TestTemplates(namespace string) TestTemplateInterface 25 | } 26 | 27 | // TestTemplateInterface has methods to work with TestTemplate resources. 28 | type TestTemplateInterface interface { 29 | Create(*v1alpha1.TestTemplate) (*v1alpha1.TestTemplate, error) 30 | Update(*v1alpha1.TestTemplate) (*v1alpha1.TestTemplate, error) 31 | Delete(name string, options *v1.DeleteOptions) error 32 | DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error 33 | Get(name string, options v1.GetOptions) (*v1alpha1.TestTemplate, error) 34 | List(opts v1.ListOptions) (*v1alpha1.TestTemplateList, error) 35 | Watch(opts v1.ListOptions) (watch.Interface, error) 36 | Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.TestTemplate, err error) 37 | TestTemplateExpansion 38 | } 39 | 40 | // testTemplates implements TestTemplateInterface 41 | type testTemplates struct { 42 | client rest.Interface 43 | ns string 44 | } 45 | 46 | // newTestTemplates returns a TestTemplates 47 | func newTestTemplates(c *SrossrossV1alpha1Client, namespace string) *testTemplates { 48 | return &testTemplates{ 49 | client: c.RESTClient(), 50 | ns: namespace, 51 | } 52 | } 53 | 54 | // Create takes the representation of a testTemplate and creates it. Returns the server's representation of the testTemplate, and an error, if there is any. 55 | func (c *testTemplates) Create(testTemplate *v1alpha1.TestTemplate) (result *v1alpha1.TestTemplate, err error) { 56 | result = &v1alpha1.TestTemplate{} 57 | err = c.client.Post(). 58 | Namespace(c.ns). 59 | Resource("testtemplates"). 60 | Body(testTemplate). 61 | Do(). 62 | Into(result) 63 | return 64 | } 65 | 66 | // Update takes the representation of a testTemplate and updates it. Returns the server's representation of the testTemplate, and an error, if there is any. 67 | func (c *testTemplates) Update(testTemplate *v1alpha1.TestTemplate) (result *v1alpha1.TestTemplate, err error) { 68 | result = &v1alpha1.TestTemplate{} 69 | err = c.client.Put(). 70 | Namespace(c.ns). 71 | Resource("testtemplates"). 72 | Name(testTemplate.Name). 73 | Body(testTemplate). 74 | Do(). 75 | Into(result) 76 | return 77 | } 78 | 79 | // Delete takes name of the testTemplate and deletes it. Returns an error if one occurs. 80 | func (c *testTemplates) Delete(name string, options *v1.DeleteOptions) error { 81 | return c.client.Delete(). 82 | Namespace(c.ns). 83 | Resource("testtemplates"). 84 | Name(name). 85 | Body(options). 86 | Do(). 87 | Error() 88 | } 89 | 90 | // DeleteCollection deletes a collection of objects. 91 | func (c *testTemplates) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { 92 | return c.client.Delete(). 93 | Namespace(c.ns). 94 | Resource("testtemplates"). 95 | VersionedParams(&listOptions, scheme.ParameterCodec). 96 | Body(options). 97 | Do(). 98 | Error() 99 | } 100 | 101 | // Get takes name of the testTemplate, and returns the corresponding testTemplate object, and an error if there is any. 102 | func (c *testTemplates) Get(name string, options v1.GetOptions) (result *v1alpha1.TestTemplate, err error) { 103 | result = &v1alpha1.TestTemplate{} 104 | err = c.client.Get(). 105 | Namespace(c.ns). 106 | Resource("testtemplates"). 107 | Name(name). 108 | VersionedParams(&options, scheme.ParameterCodec). 109 | Do(). 110 | Into(result) 111 | return 112 | } 113 | 114 | // List takes label and field selectors, and returns the list of TestTemplates that match those selectors. 115 | func (c *testTemplates) List(opts v1.ListOptions) (result *v1alpha1.TestTemplateList, err error) { 116 | result = &v1alpha1.TestTemplateList{} 117 | err = c.client.Get(). 118 | Namespace(c.ns). 119 | Resource("testtemplates"). 120 | VersionedParams(&opts, scheme.ParameterCodec). 121 | Do(). 122 | Into(result) 123 | return 124 | } 125 | 126 | // Watch returns a watch.Interface that watches the requested testTemplates. 127 | func (c *testTemplates) Watch(opts v1.ListOptions) (watch.Interface, error) { 128 | opts.Watch = true 129 | return c.client.Get(). 130 | Namespace(c.ns). 131 | Resource("testtemplates"). 132 | VersionedParams(&opts, scheme.ParameterCodec). 133 | Watch() 134 | } 135 | 136 | // Patch applies the patch and returns the patched testTemplate. 137 | func (c *testTemplates) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.TestTemplate, err error) { 138 | result = &v1alpha1.TestTemplate{} 139 | err = c.client.Patch(pt). 140 | Namespace(c.ns). 141 | Resource("testtemplates"). 142 | SubResource(subresources...). 143 | Name(name). 144 | Body(data). 145 | Do(). 146 | Into(result) 147 | return 148 | } 149 | -------------------------------------------------------------------------------- /pkg/controller/controller.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | corev1 "k8s.io/api/core/v1" 5 | "k8s.io/apimachinery/pkg/labels" 6 | typedv1 "k8s.io/client-go/kubernetes/typed/core/v1" 7 | listerv1 "k8s.io/client-go/listers/core/v1" 8 | cache "k8s.io/client-go/tools/cache" 9 | 10 | v1alpha1 "github.com/srossross/k8s-test-controller/pkg/apis/tester/v1alpha1" 11 | client "github.com/srossross/k8s-test-controller/pkg/client" 12 | srossrossv1alpha1 "github.com/srossross/k8s-test-controller/pkg/client/typed/srossross/v1alpha1" 13 | factory "github.com/srossross/k8s-test-controller/pkg/informers/externalversions" 14 | listerV1alpha1 "github.com/srossross/k8s-test-controller/pkg/listers/srossross/v1alpha1" 15 | ) 16 | 17 | // Interface for a TestController 18 | type Interface interface { 19 | TestTemplateLister() listerV1alpha1.TestTemplateLister 20 | TestRunLister() listerV1alpha1.TestRunLister 21 | SrossrossV1alpha1() srossrossv1alpha1.SrossrossV1alpha1Interface 22 | 23 | PodInformer() cache.SharedIndexInformer 24 | CoreV1() typedv1.CoreV1Interface 25 | 26 | GetTestRunFromKey(key string) (*v1alpha1.TestRun, error) 27 | GetPodAndTestRunFromKey(key string) (*v1alpha1.TestRun, *corev1.Pod, error) 28 | TestRunnerRemovePodsForDeletedTest(key string) error 29 | 30 | CreatePod(Namespace string, pod *corev1.Pod) (*corev1.Pod, error) 31 | ListPods(Namespace string, selector labels.Selector) ([]*corev1.Pod, error) 32 | GetPod(Namespace string, Name string) (*corev1.Pod, error) 33 | } 34 | 35 | // TestController creates a single interface to run the reconsile loop 36 | type TestController struct { 37 | sharedFactory *factory.SharedInformerFactory 38 | testClient client.Interface 39 | coreV1Client typedv1.CoreV1Interface 40 | } 41 | 42 | // CoreV1 get CoreV1 client 43 | func (ctrl *TestController) CoreV1() typedv1.CoreV1Interface { 44 | if ctrl == nil { 45 | return nil 46 | } 47 | return ctrl.coreV1Client 48 | } 49 | 50 | // SrossrossV1alpha1 get SrossrossV1alpha1 client 51 | func (ctrl *TestController) SrossrossV1alpha1() srossrossv1alpha1.SrossrossV1alpha1Interface { 52 | return ctrl.testClient.SrossrossV1alpha1() 53 | } 54 | 55 | // TestTemplateLister get a testlister 56 | func (ctrl *TestController) TestTemplateLister() listerV1alpha1.TestTemplateLister { 57 | return (*ctrl.sharedFactory).Srossross().V1alpha1().TestTemplates().Lister() 58 | } 59 | 60 | // TestRunLister get a testrun lister 61 | func (ctrl *TestController) TestRunLister() listerV1alpha1.TestRunLister { 62 | return (*ctrl.sharedFactory).Srossross().V1alpha1().TestRuns().Lister() 63 | } 64 | 65 | // PodInformer returns an informer for a pod and registers it with the SharedInformerFactory 66 | func (ctrl *TestController) PodInformer() cache.SharedIndexInformer { 67 | return (*ctrl.sharedFactory).InformerFor(&corev1.Pod{}, ctrl.newPodInformerFactory()) 68 | } 69 | 70 | // PodLister gets a corev1 podlister 71 | func (ctrl *TestController) PodLister() listerv1.PodLister { 72 | return listerv1.NewPodLister(ctrl.PodInformer().GetIndexer()) 73 | } 74 | 75 | func (ctrl *TestController) CreatePod(Namespace string, pod *corev1.Pod) (*corev1.Pod, error) { 76 | return ctrl.CoreV1().Pods(Namespace).Create(pod) 77 | } 78 | 79 | func (ctrl *TestController) ListPods(Namespace string, selector labels.Selector) ([]*corev1.Pod, error) { 80 | return ctrl.PodLister().Pods(Namespace).List(selector) 81 | } 82 | 83 | func (ctrl *TestController) GetPod(Namespace string, Name string) (*corev1.Pod, error) { 84 | return ctrl.PodLister().Pods(Namespace).Get(Name) 85 | } 86 | 87 | //NewTestController creates a new TestController 88 | func NewTestController(sharedFactory *factory.SharedInformerFactory, testClient client.Interface, coreV1Client typedv1.CoreV1Interface) Interface { 89 | return &TestController{ 90 | sharedFactory: sharedFactory, 91 | testClient: testClient, 92 | coreV1Client: coreV1Client, 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /pkg/controller/controller_test.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | assert "github.com/stretchr/testify/assert" 8 | 9 | runtime "k8s.io/apimachinery/pkg/runtime" 10 | fakev1 "k8s.io/client-go/kubernetes/typed/core/v1/fake" 11 | 12 | fake "github.com/srossross/k8s-test-controller/pkg/client/fake" 13 | factory "github.com/srossross/k8s-test-controller/pkg/informers/externalversions" 14 | ) 15 | 16 | func createFakeController(objects ...runtime.Object) Interface { 17 | cl := fake.NewSimpleClientset(objects...) 18 | coreV1Client := &fakev1.FakeCoreV1{} 19 | sharedFactory := factory.NewSharedInformerFactory(cl, time.Second*30) 20 | return NewTestController(&sharedFactory, cl, coreV1Client) 21 | } 22 | 23 | func TestFakeController(t *testing.T) { 24 | assert := assert.New(t) 25 | 26 | ctrl := createFakeController() 27 | 28 | assert.NotNil(ctrl) 29 | } 30 | -------------------------------------------------------------------------------- /pkg/controller/fake/fake.go: -------------------------------------------------------------------------------- 1 | package fake 2 | 3 | import ( 4 | corev1 "k8s.io/api/core/v1" 5 | "k8s.io/apimachinery/pkg/labels" 6 | typedv1 "k8s.io/client-go/kubernetes/typed/core/v1" 7 | cache "k8s.io/client-go/tools/cache" 8 | 9 | v1alpha1 "github.com/srossross/k8s-test-controller/pkg/apis/tester/v1alpha1" 10 | srossrossv1alpha1 "github.com/srossross/k8s-test-controller/pkg/client/typed/srossross/v1alpha1" 11 | controller "github.com/srossross/k8s-test-controller/pkg/controller" 12 | listerV1alpha1 "github.com/srossross/k8s-test-controller/pkg/listers/srossross/v1alpha1" 13 | ) 14 | 15 | type fakeController struct { 16 | } 17 | 18 | func (f *fakeController) TestTemplateLister() listerV1alpha1.TestTemplateLister { 19 | return nil 20 | } 21 | 22 | func (f *fakeController) TestRunLister() listerV1alpha1.TestRunLister { 23 | return nil 24 | } 25 | 26 | func (f *fakeController) SrossrossV1alpha1() srossrossv1alpha1.SrossrossV1alpha1Interface { 27 | return nil 28 | } 29 | 30 | func (f *fakeController) PodInformer() cache.SharedIndexInformer { 31 | return nil 32 | } 33 | func (f *fakeController) CoreV1() typedv1.CoreV1Interface { 34 | return nil 35 | } 36 | 37 | func (f *fakeController) GetTestRunFromKey(key string) (*v1alpha1.TestRun, error) { 38 | return nil, nil 39 | } 40 | func (f *fakeController) GetPodAndTestRunFromKey(key string) (*v1alpha1.TestRun, *corev1.Pod, error) { 41 | return nil, nil, nil 42 | } 43 | func (f *fakeController) TestRunnerRemovePodsForDeletedTest(key string) error { 44 | return nil 45 | } 46 | 47 | func (f *fakeController) CreatePod(Namespace string, pod *corev1.Pod) (*corev1.Pod, error) { 48 | return nil, nil 49 | } 50 | func (f *fakeController) ListPods(Namespace string, selector labels.Selector) ([]*corev1.Pod, error) { 51 | return nil, nil 52 | } 53 | func (f *fakeController) GetPod(Namespace string, Name string) (*corev1.Pod, error) { 54 | return nil, nil 55 | } 56 | 57 | // NewController this is a fake 58 | func NewController() controller.Interface { 59 | return &fakeController{} 60 | } 61 | -------------------------------------------------------------------------------- /pkg/controller/filters.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "k8s.io/api/core/v1" 5 | ) 6 | 7 | // TestRunFilter filter pods that were instantiated with testrun of name 8 | // testRunName 9 | func TestRunFilter(pods []*v1.Pod, testRunName string) []*v1.Pod { 10 | result := []*v1.Pod{} 11 | for _, pod := range pods { 12 | if pod.Labels["test-run"] == testRunName { 13 | result = append(result, pod) 14 | } 15 | // if(!strings.HasPrefix(a[i], "foo_") && len(a[i]) <= 7) { 16 | // nofoos = append(nofoos, a[i]) 17 | } 18 | 19 | return result 20 | } 21 | -------------------------------------------------------------------------------- /pkg/controller/filters_test.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | v1 "k8s.io/api/core/v1" 8 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 9 | ) 10 | 11 | func TestFilterPodsEmpty(t *testing.T) { 12 | assert := assert.New(t) 13 | 14 | reducedPods := TestRunFilter([]*v1.Pod{}, "tr1") 15 | assert.Equal(len(reducedPods), 0, "The length after filtering should be 0") 16 | } 17 | 18 | func TestFilterPods(t *testing.T) { 19 | assert := assert.New(t) 20 | pods := []*v1.Pod{ 21 | &v1.Pod{ 22 | ObjectMeta: metav1.ObjectMeta{ 23 | Labels: map[string]string{"test-run": "tr1"}, 24 | }, 25 | }, 26 | &v1.Pod{ 27 | ObjectMeta: metav1.ObjectMeta{ 28 | Labels: map[string]string{"test-run": "tr2"}, 29 | }, 30 | }, 31 | } 32 | reducedPods := TestRunFilter(pods, "tr1") 33 | assert.Equal(len(reducedPods), 1, "Should only have one test with testrun tr1") 34 | } 35 | -------------------------------------------------------------------------------- /pkg/controller/informer_utils.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "time" 5 | 6 | factory_interfaces "github.com/srossross/k8s-test-controller/pkg/informers/externalversions/internalinterfaces" 7 | 8 | client "github.com/srossross/k8s-test-controller/pkg/client" 9 | corev1 "k8s.io/api/core/v1" 10 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 11 | pkgRuntime "k8s.io/apimachinery/pkg/runtime" 12 | watch "k8s.io/apimachinery/pkg/watch" 13 | cache "k8s.io/client-go/tools/cache" 14 | ) 15 | 16 | func (ctrl *TestController) newPodInformerFactory() factory_interfaces.NewInformerFunc { 17 | return func(cl client.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { 18 | sharedIndexInformer := cache.NewSharedIndexInformer( 19 | &cache.ListWatch{ 20 | ListFunc: func(options metav1.ListOptions) (pkgRuntime.Object, error) { 21 | return ctrl.CoreV1().Pods(metav1.NamespaceAll).List(options) 22 | }, 23 | WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { 24 | return ctrl.CoreV1().Pods(metav1.NamespaceAll).Watch(options) 25 | }, 26 | }, 27 | &corev1.Pod{}, 28 | resyncPeriod, 29 | cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, 30 | ) 31 | return sharedIndexInformer 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /pkg/controller/keys.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | v1alpha1 "github.com/srossross/k8s-test-controller/pkg/apis/tester/v1alpha1" 8 | "k8s.io/api/core/v1" 9 | ) 10 | 11 | func splitOnce(key, sep string) (string, string) { 12 | tmp := strings.SplitN(key, sep, 2) 13 | if len(tmp) == 1 { 14 | return tmp[0], "" 15 | } 16 | return tmp[0], tmp[1] 17 | } 18 | 19 | // GetTestRunFromKey get a test run from a key put on the queue 20 | func (ctrl *TestController) GetTestRunFromKey(key string) (*v1alpha1.TestRun, error) { 21 | namespace, name := splitOnce(key, "/") 22 | fmt.Printf("'%s', '%s'", namespace, name) 23 | return ctrl.TestRunLister().TestRuns(namespace).Get(name) 24 | } 25 | 26 | // GetPodAndTestRunFromKey get a test run from a key put on the queue 27 | func (ctrl *TestController) GetPodAndTestRunFromKey(key string) (*v1alpha1.TestRun, *v1.Pod, error) { 28 | 29 | namespace, name := splitOnce(key, "/") 30 | 31 | pod, err := ctrl.PodLister().Pods(namespace).Get(name) 32 | if err != nil { 33 | return nil, nil, err 34 | } 35 | 36 | testRun, err := ctrl.TestRunLister().TestRuns(namespace).Get(pod.Labels["test-run"]) 37 | 38 | if err != nil { 39 | return nil, nil, err 40 | } 41 | 42 | return testRun, pod, nil 43 | 44 | } 45 | -------------------------------------------------------------------------------- /pkg/controller/keys_test.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "testing" 5 | 6 | assert "github.com/stretchr/testify/assert" 7 | 8 | errors "k8s.io/apimachinery/pkg/api/errors" 9 | // metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 10 | // v1alpha1 "github.com/srossross/k8s-test-controller/pkg/apis/tester/v1alpha1" 11 | ) 12 | 13 | func TestSplitOnce(t *testing.T) { 14 | // t.Log("Oh noes - something is false") 15 | // t.Fail() 16 | a, b := splitOnce("key:value", ":") 17 | if a != "key" { 18 | t.Fatalf("a should equal 'key' (got %v)", a) 19 | } 20 | if b != "value" { 21 | t.Fatalf("a should equal 'value' (got %v)", a) 22 | } 23 | 24 | return 25 | } 26 | 27 | func TestGetTestRunFromKeyWhenEmpty(t *testing.T) { 28 | assert := assert.New(t) 29 | 30 | ctrl := createFakeController() 31 | 32 | tr, err := ctrl.GetTestRunFromKey("ns/key") 33 | 34 | assert.NotNil(err) 35 | assert.True(errors.IsNotFound(err)) 36 | assert.Nil(tr) 37 | } 38 | 39 | // func TestGetTestRunFromKey(t *testing.T) { 40 | // assert := assert.New(t) 41 | // 42 | // ctrl := createFakeController( 43 | // &v1alpha1.TestRun{ 44 | // ObjectMeta: metav1.ObjectMeta{ 45 | // Namespace: "foo", 46 | // Name: "run-42", 47 | // }, 48 | // }, 49 | // ) 50 | // 51 | // tr, err := ctrl.GetTestRunFromKey("foo/run-42") 52 | // 53 | // assert.Nil(err) 54 | // assert.NotNil(tr) 55 | // } 56 | -------------------------------------------------------------------------------- /pkg/controller/remove_pods.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | labels "k8s.io/apimachinery/pkg/labels" 9 | ) 10 | 11 | // TestRunnerRemovePodsForDeletedTest will remove pods with test as owner 12 | func (ctrl *TestController) TestRunnerRemovePodsForDeletedTest( 13 | key string, 14 | ) error { 15 | log.Printf(" | Delete pods for removed test runner") 16 | Namespace, Name := splitOnce(key, "/") 17 | 18 | pods, err := ctrl.PodLister().Pods(Namespace).List(labels.Everything()) 19 | if err != nil { 20 | return fmt.Errorf("Error getting list of pods: %s", err.Error()) 21 | } 22 | 23 | pods = TestRunFilter(pods, Name) 24 | log.Printf(" | Found %v pods to delete in namespace %s", len(pods), Namespace) 25 | 26 | for _, pod := range pods { 27 | log.Printf(" | Removing pod '%s/%s'", pod.Namespace, pod.Name) 28 | err := ctrl.CoreV1().Pods(pod.Namespace).Delete(pod.Name, &metav1.DeleteOptions{}) 29 | if err != nil { 30 | return fmt.Errorf("Error removing pod: %s", err.Error()) 31 | } 32 | } 33 | return nil 34 | } 35 | -------------------------------------------------------------------------------- /pkg/informers/externalversions/factory.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Sean Ross-Ross 6 | 7 | See License in the root of this repo. 8 | 9 | */ 10 | 11 | // This file was automatically generated by informer-gen 12 | 13 | package externalversions 14 | 15 | import ( 16 | client "github.com/srossross/k8s-test-controller/pkg/client" 17 | internalinterfaces "github.com/srossross/k8s-test-controller/pkg/informers/externalversions/internalinterfaces" 18 | srossross "github.com/srossross/k8s-test-controller/pkg/informers/externalversions/srossross" 19 | runtime "k8s.io/apimachinery/pkg/runtime" 20 | schema "k8s.io/apimachinery/pkg/runtime/schema" 21 | cache "k8s.io/client-go/tools/cache" 22 | reflect "reflect" 23 | sync "sync" 24 | time "time" 25 | ) 26 | 27 | type sharedInformerFactory struct { 28 | client client.Interface 29 | lock sync.Mutex 30 | defaultResync time.Duration 31 | 32 | informers map[reflect.Type]cache.SharedIndexInformer 33 | // startedInformers is used for tracking which informers have been started. 34 | // This allows Start() to be called multiple times safely. 35 | startedInformers map[reflect.Type]bool 36 | } 37 | 38 | // NewSharedInformerFactory constructs a new instance of sharedInformerFactory 39 | func NewSharedInformerFactory(client client.Interface, defaultResync time.Duration) SharedInformerFactory { 40 | return &sharedInformerFactory{ 41 | client: client, 42 | defaultResync: defaultResync, 43 | informers: make(map[reflect.Type]cache.SharedIndexInformer), 44 | startedInformers: make(map[reflect.Type]bool), 45 | } 46 | } 47 | 48 | // Start initializes all requested informers. 49 | func (f *sharedInformerFactory) Start(stopCh <-chan struct{}) { 50 | f.lock.Lock() 51 | defer f.lock.Unlock() 52 | 53 | for informerType, informer := range f.informers { 54 | if !f.startedInformers[informerType] { 55 | go informer.Run(stopCh) 56 | f.startedInformers[informerType] = true 57 | } 58 | } 59 | } 60 | 61 | // WaitForCacheSync waits for all started informers' cache were synced. 62 | func (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool { 63 | informers := func() map[reflect.Type]cache.SharedIndexInformer { 64 | f.lock.Lock() 65 | defer f.lock.Unlock() 66 | 67 | informers := map[reflect.Type]cache.SharedIndexInformer{} 68 | for informerType, informer := range f.informers { 69 | if f.startedInformers[informerType] { 70 | informers[informerType] = informer 71 | } 72 | } 73 | return informers 74 | }() 75 | 76 | res := map[reflect.Type]bool{} 77 | for informType, informer := range informers { 78 | res[informType] = cache.WaitForCacheSync(stopCh, informer.HasSynced) 79 | } 80 | return res 81 | } 82 | 83 | // InternalInformerFor returns the SharedIndexInformer for obj using an internal 84 | // client. 85 | func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer { 86 | f.lock.Lock() 87 | defer f.lock.Unlock() 88 | 89 | informerType := reflect.TypeOf(obj) 90 | informer, exists := f.informers[informerType] 91 | if exists { 92 | return informer 93 | } 94 | informer = newFunc(f.client, f.defaultResync) 95 | f.informers[informerType] = informer 96 | 97 | return informer 98 | } 99 | 100 | // SharedInformerFactory provides shared informers for resources in all known 101 | // API group versions. 102 | type SharedInformerFactory interface { 103 | internalinterfaces.SharedInformerFactory 104 | ForResource(resource schema.GroupVersionResource) (GenericInformer, error) 105 | WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool 106 | 107 | Srossross() srossross.Interface 108 | } 109 | 110 | func (f *sharedInformerFactory) Srossross() srossross.Interface { 111 | return srossross.New(f) 112 | } 113 | -------------------------------------------------------------------------------- /pkg/informers/externalversions/generic.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Sean Ross-Ross 6 | 7 | See License in the root of this repo. 8 | 9 | */ 10 | 11 | // This file was automatically generated by informer-gen 12 | 13 | package externalversions 14 | 15 | import ( 16 | "fmt" 17 | v1alpha1 "github.com/srossross/k8s-test-controller/pkg/apis/tester/v1alpha1" 18 | schema "k8s.io/apimachinery/pkg/runtime/schema" 19 | cache "k8s.io/client-go/tools/cache" 20 | ) 21 | 22 | // GenericInformer is type of SharedIndexInformer which will locate and delegate to other 23 | // sharedInformers based on type 24 | type GenericInformer interface { 25 | Informer() cache.SharedIndexInformer 26 | Lister() cache.GenericLister 27 | } 28 | 29 | type genericInformer struct { 30 | informer cache.SharedIndexInformer 31 | resource schema.GroupResource 32 | } 33 | 34 | // Informer returns the SharedIndexInformer. 35 | func (f *genericInformer) Informer() cache.SharedIndexInformer { 36 | return f.informer 37 | } 38 | 39 | // Lister returns the GenericLister. 40 | func (f *genericInformer) Lister() cache.GenericLister { 41 | return cache.NewGenericLister(f.Informer().GetIndexer(), f.resource) 42 | } 43 | 44 | // ForResource gives generic access to a shared informer of the matching type 45 | // TODO extend this to unknown resources with a client pool 46 | func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) { 47 | switch resource { 48 | // Group=Srossross, Version=V1alpha1 49 | case v1alpha1.SchemeGroupVersion.WithResource("testruns"): 50 | return &genericInformer{resource: resource.GroupResource(), informer: f.Srossross().V1alpha1().TestRuns().Informer()}, nil 51 | case v1alpha1.SchemeGroupVersion.WithResource("testtemplates"): 52 | return &genericInformer{resource: resource.GroupResource(), informer: f.Srossross().V1alpha1().TestTemplates().Informer()}, nil 53 | 54 | } 55 | 56 | return nil, fmt.Errorf("no informer found for %v", resource) 57 | } 58 | -------------------------------------------------------------------------------- /pkg/informers/externalversions/internalinterfaces/factory_interfaces.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Sean Ross-Ross 6 | 7 | See License in the root of this repo. 8 | 9 | */ 10 | 11 | // This file was automatically generated by informer-gen 12 | 13 | package internalinterfaces 14 | 15 | import ( 16 | client "github.com/srossross/k8s-test-controller/pkg/client" 17 | runtime "k8s.io/apimachinery/pkg/runtime" 18 | cache "k8s.io/client-go/tools/cache" 19 | time "time" 20 | ) 21 | 22 | type NewInformerFunc func(client.Interface, time.Duration) cache.SharedIndexInformer 23 | 24 | // SharedInformerFactory a small interface to allow for adding an informer without an import cycle 25 | type SharedInformerFactory interface { 26 | Start(stopCh <-chan struct{}) 27 | InformerFor(obj runtime.Object, newFunc NewInformerFunc) cache.SharedIndexInformer 28 | } 29 | -------------------------------------------------------------------------------- /pkg/informers/externalversions/srossross/interface.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Sean Ross-Ross 6 | 7 | See License in the root of this repo. 8 | 9 | */ 10 | 11 | // This file was automatically generated by informer-gen 12 | 13 | package srossross 14 | 15 | import ( 16 | internalinterfaces "github.com/srossross/k8s-test-controller/pkg/informers/externalversions/internalinterfaces" 17 | v1alpha1 "github.com/srossross/k8s-test-controller/pkg/informers/externalversions/srossross/v1alpha1" 18 | ) 19 | 20 | // Interface provides access to each of this group's versions. 21 | type Interface interface { 22 | // V1alpha1 provides access to shared informers for resources in V1alpha1. 23 | V1alpha1() v1alpha1.Interface 24 | } 25 | 26 | type group struct { 27 | internalinterfaces.SharedInformerFactory 28 | } 29 | 30 | // New returns a new Interface. 31 | func New(f internalinterfaces.SharedInformerFactory) Interface { 32 | return &group{f} 33 | } 34 | 35 | // V1alpha1 returns a new v1alpha1.Interface. 36 | func (g *group) V1alpha1() v1alpha1.Interface { 37 | return v1alpha1.New(g.SharedInformerFactory) 38 | } 39 | -------------------------------------------------------------------------------- /pkg/informers/externalversions/srossross/v1alpha1/interface.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Sean Ross-Ross 6 | 7 | See License in the root of this repo. 8 | 9 | */ 10 | 11 | // This file was automatically generated by informer-gen 12 | 13 | package v1alpha1 14 | 15 | import ( 16 | internalinterfaces "github.com/srossross/k8s-test-controller/pkg/informers/externalversions/internalinterfaces" 17 | ) 18 | 19 | // Interface provides access to all the informers in this group version. 20 | type Interface interface { 21 | // TestRuns returns a TestRunInformer. 22 | TestRuns() TestRunInformer 23 | // TestTemplates returns a TestTemplateInformer. 24 | TestTemplates() TestTemplateInformer 25 | } 26 | 27 | type version struct { 28 | internalinterfaces.SharedInformerFactory 29 | } 30 | 31 | // New returns a new Interface. 32 | func New(f internalinterfaces.SharedInformerFactory) Interface { 33 | return &version{f} 34 | } 35 | 36 | // TestRuns returns a TestRunInformer. 37 | func (v *version) TestRuns() TestRunInformer { 38 | return &testRunInformer{factory: v.SharedInformerFactory} 39 | } 40 | 41 | // TestTemplates returns a TestTemplateInformer. 42 | func (v *version) TestTemplates() TestTemplateInformer { 43 | return &testTemplateInformer{factory: v.SharedInformerFactory} 44 | } 45 | -------------------------------------------------------------------------------- /pkg/informers/externalversions/srossross/v1alpha1/testrun.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Sean Ross-Ross 6 | 7 | See License in the root of this repo. 8 | 9 | */ 10 | 11 | // This file was automatically generated by informer-gen 12 | 13 | package v1alpha1 14 | 15 | import ( 16 | tester_v1alpha1 "github.com/srossross/k8s-test-controller/pkg/apis/tester/v1alpha1" 17 | client "github.com/srossross/k8s-test-controller/pkg/client" 18 | internalinterfaces "github.com/srossross/k8s-test-controller/pkg/informers/externalversions/internalinterfaces" 19 | v1alpha1 "github.com/srossross/k8s-test-controller/pkg/listers/srossross/v1alpha1" 20 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 21 | runtime "k8s.io/apimachinery/pkg/runtime" 22 | watch "k8s.io/apimachinery/pkg/watch" 23 | cache "k8s.io/client-go/tools/cache" 24 | time "time" 25 | ) 26 | 27 | // TestRunInformer provides access to a shared informer and lister for 28 | // TestRuns. 29 | type TestRunInformer interface { 30 | Informer() cache.SharedIndexInformer 31 | Lister() v1alpha1.TestRunLister 32 | } 33 | 34 | type testRunInformer struct { 35 | factory internalinterfaces.SharedInformerFactory 36 | } 37 | 38 | func newTestRunInformer(client client.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { 39 | sharedIndexInformer := cache.NewSharedIndexInformer( 40 | &cache.ListWatch{ 41 | ListFunc: func(options v1.ListOptions) (runtime.Object, error) { 42 | return client.SrossrossV1alpha1().TestRuns(v1.NamespaceAll).List(options) 43 | }, 44 | WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { 45 | return client.SrossrossV1alpha1().TestRuns(v1.NamespaceAll).Watch(options) 46 | }, 47 | }, 48 | &tester_v1alpha1.TestRun{}, 49 | resyncPeriod, 50 | cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, 51 | ) 52 | 53 | return sharedIndexInformer 54 | } 55 | 56 | func (f *testRunInformer) Informer() cache.SharedIndexInformer { 57 | return f.factory.InformerFor(&tester_v1alpha1.TestRun{}, newTestRunInformer) 58 | } 59 | 60 | func (f *testRunInformer) Lister() v1alpha1.TestRunLister { 61 | return v1alpha1.NewTestRunLister(f.Informer().GetIndexer()) 62 | } 63 | -------------------------------------------------------------------------------- /pkg/informers/externalversions/srossross/v1alpha1/testtemplate.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Sean Ross-Ross 6 | 7 | See License in the root of this repo. 8 | 9 | */ 10 | 11 | // This file was automatically generated by informer-gen 12 | 13 | package v1alpha1 14 | 15 | import ( 16 | tester_v1alpha1 "github.com/srossross/k8s-test-controller/pkg/apis/tester/v1alpha1" 17 | client "github.com/srossross/k8s-test-controller/pkg/client" 18 | internalinterfaces "github.com/srossross/k8s-test-controller/pkg/informers/externalversions/internalinterfaces" 19 | v1alpha1 "github.com/srossross/k8s-test-controller/pkg/listers/srossross/v1alpha1" 20 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 21 | runtime "k8s.io/apimachinery/pkg/runtime" 22 | watch "k8s.io/apimachinery/pkg/watch" 23 | cache "k8s.io/client-go/tools/cache" 24 | time "time" 25 | ) 26 | 27 | // TestTemplateInformer provides access to a shared informer and lister for 28 | // TestTemplates. 29 | type TestTemplateInformer interface { 30 | Informer() cache.SharedIndexInformer 31 | Lister() v1alpha1.TestTemplateLister 32 | } 33 | 34 | type testTemplateInformer struct { 35 | factory internalinterfaces.SharedInformerFactory 36 | } 37 | 38 | func newTestTemplateInformer(client client.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { 39 | sharedIndexInformer := cache.NewSharedIndexInformer( 40 | &cache.ListWatch{ 41 | ListFunc: func(options v1.ListOptions) (runtime.Object, error) { 42 | return client.SrossrossV1alpha1().TestTemplates(v1.NamespaceAll).List(options) 43 | }, 44 | WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { 45 | return client.SrossrossV1alpha1().TestTemplates(v1.NamespaceAll).Watch(options) 46 | }, 47 | }, 48 | &tester_v1alpha1.TestTemplate{}, 49 | resyncPeriod, 50 | cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, 51 | ) 52 | 53 | return sharedIndexInformer 54 | } 55 | 56 | func (f *testTemplateInformer) Informer() cache.SharedIndexInformer { 57 | return f.factory.InformerFor(&tester_v1alpha1.TestTemplate{}, newTestTemplateInformer) 58 | } 59 | 60 | func (f *testTemplateInformer) Lister() v1alpha1.TestTemplateLister { 61 | return v1alpha1.NewTestTemplateLister(f.Informer().GetIndexer()) 62 | } 63 | -------------------------------------------------------------------------------- /pkg/informers/internalversion/factory.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Sean Ross-Ross 6 | 7 | See License in the root of this repo. 8 | 9 | */ 10 | 11 | // This file was automatically generated by informer-gen 12 | 13 | package internalversion 14 | 15 | import ( 16 | internalclientset "github.com/srossross/k8s-test-controller/pkg/client/internalclientset" 17 | internalinterfaces "github.com/srossross/k8s-test-controller/pkg/informers/internalversion/internalinterfaces" 18 | runtime "k8s.io/apimachinery/pkg/runtime" 19 | schema "k8s.io/apimachinery/pkg/runtime/schema" 20 | cache "k8s.io/client-go/tools/cache" 21 | reflect "reflect" 22 | sync "sync" 23 | time "time" 24 | ) 25 | 26 | type sharedInformerFactory struct { 27 | client internalclientset.Interface 28 | lock sync.Mutex 29 | defaultResync time.Duration 30 | 31 | informers map[reflect.Type]cache.SharedIndexInformer 32 | // startedInformers is used for tracking which informers have been started. 33 | // This allows Start() to be called multiple times safely. 34 | startedInformers map[reflect.Type]bool 35 | } 36 | 37 | // NewSharedInformerFactory constructs a new instance of sharedInformerFactory 38 | func NewSharedInformerFactory(client internalclientset.Interface, defaultResync time.Duration) SharedInformerFactory { 39 | return &sharedInformerFactory{ 40 | client: client, 41 | defaultResync: defaultResync, 42 | informers: make(map[reflect.Type]cache.SharedIndexInformer), 43 | startedInformers: make(map[reflect.Type]bool), 44 | } 45 | } 46 | 47 | // Start initializes all requested informers. 48 | func (f *sharedInformerFactory) Start(stopCh <-chan struct{}) { 49 | f.lock.Lock() 50 | defer f.lock.Unlock() 51 | 52 | for informerType, informer := range f.informers { 53 | if !f.startedInformers[informerType] { 54 | go informer.Run(stopCh) 55 | f.startedInformers[informerType] = true 56 | } 57 | } 58 | } 59 | 60 | // WaitForCacheSync waits for all started informers' cache were synced. 61 | func (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool { 62 | informers := func() map[reflect.Type]cache.SharedIndexInformer { 63 | f.lock.Lock() 64 | defer f.lock.Unlock() 65 | 66 | informers := map[reflect.Type]cache.SharedIndexInformer{} 67 | for informerType, informer := range f.informers { 68 | if f.startedInformers[informerType] { 69 | informers[informerType] = informer 70 | } 71 | } 72 | return informers 73 | }() 74 | 75 | res := map[reflect.Type]bool{} 76 | for informType, informer := range informers { 77 | res[informType] = cache.WaitForCacheSync(stopCh, informer.HasSynced) 78 | } 79 | return res 80 | } 81 | 82 | // InternalInformerFor returns the SharedIndexInformer for obj using an internal 83 | // client. 84 | func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer { 85 | f.lock.Lock() 86 | defer f.lock.Unlock() 87 | 88 | informerType := reflect.TypeOf(obj) 89 | informer, exists := f.informers[informerType] 90 | if exists { 91 | return informer 92 | } 93 | informer = newFunc(f.client, f.defaultResync) 94 | f.informers[informerType] = informer 95 | 96 | return informer 97 | } 98 | 99 | // SharedInformerFactory provides shared informers for resources in all known 100 | // API group versions. 101 | type SharedInformerFactory interface { 102 | internalinterfaces.SharedInformerFactory 103 | ForResource(resource schema.GroupVersionResource) (GenericInformer, error) 104 | WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool 105 | } 106 | -------------------------------------------------------------------------------- /pkg/informers/internalversion/generic.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Sean Ross-Ross 6 | 7 | See License in the root of this repo. 8 | 9 | */ 10 | 11 | // This file was automatically generated by informer-gen 12 | 13 | package internalversion 14 | 15 | import ( 16 | "fmt" 17 | schema "k8s.io/apimachinery/pkg/runtime/schema" 18 | cache "k8s.io/client-go/tools/cache" 19 | ) 20 | 21 | // GenericInformer is type of SharedIndexInformer which will locate and delegate to other 22 | // sharedInformers based on type 23 | type GenericInformer interface { 24 | Informer() cache.SharedIndexInformer 25 | Lister() cache.GenericLister 26 | } 27 | 28 | type genericInformer struct { 29 | informer cache.SharedIndexInformer 30 | resource schema.GroupResource 31 | } 32 | 33 | // Informer returns the SharedIndexInformer. 34 | func (f *genericInformer) Informer() cache.SharedIndexInformer { 35 | return f.informer 36 | } 37 | 38 | // Lister returns the GenericLister. 39 | func (f *genericInformer) Lister() cache.GenericLister { 40 | return cache.NewGenericLister(f.Informer().GetIndexer(), f.resource) 41 | } 42 | 43 | // ForResource gives generic access to a shared informer of the matching type 44 | // TODO extend this to unknown resources with a client pool 45 | func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) { 46 | switch resource { 47 | } 48 | 49 | return nil, fmt.Errorf("no informer found for %v", resource) 50 | } 51 | -------------------------------------------------------------------------------- /pkg/informers/internalversion/internalinterfaces/factory_interfaces.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Sean Ross-Ross 6 | 7 | See License in the root of this repo. 8 | 9 | */ 10 | 11 | // This file was automatically generated by informer-gen 12 | 13 | package internalinterfaces 14 | 15 | import ( 16 | internalclientset "github.com/srossross/k8s-test-controller/pkg/client/internalclientset" 17 | runtime "k8s.io/apimachinery/pkg/runtime" 18 | cache "k8s.io/client-go/tools/cache" 19 | time "time" 20 | ) 21 | 22 | type NewInformerFunc func(internalclientset.Interface, time.Duration) cache.SharedIndexInformer 23 | 24 | // SharedInformerFactory a small interface to allow for adding an informer without an import cycle 25 | type SharedInformerFactory interface { 26 | Start(stopCh <-chan struct{}) 27 | InformerFor(obj runtime.Object, newFunc NewInformerFunc) cache.SharedIndexInformer 28 | } 29 | -------------------------------------------------------------------------------- /pkg/listers/srossross/v1alpha1/expansion_generated.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Sean Ross-Ross 6 | 7 | See License in the root of this repo. 8 | 9 | */ 10 | 11 | // This file was automatically generated by lister-gen 12 | 13 | package v1alpha1 14 | 15 | // TestRunListerExpansion allows custom methods to be added to 16 | // TestRunLister. 17 | type TestRunListerExpansion interface{} 18 | 19 | // TestRunNamespaceListerExpansion allows custom methods to be added to 20 | // TestRunNamespaceLister. 21 | type TestRunNamespaceListerExpansion interface{} 22 | 23 | // TestTemplateListerExpansion allows custom methods to be added to 24 | // TestTemplateLister. 25 | type TestTemplateListerExpansion interface{} 26 | 27 | // TestTemplateNamespaceListerExpansion allows custom methods to be added to 28 | // TestTemplateNamespaceLister. 29 | type TestTemplateNamespaceListerExpansion interface{} 30 | -------------------------------------------------------------------------------- /pkg/listers/srossross/v1alpha1/testrun.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Sean Ross-Ross 6 | 7 | See License in the root of this repo. 8 | 9 | */ 10 | 11 | // This file was automatically generated by lister-gen 12 | 13 | package v1alpha1 14 | 15 | import ( 16 | v1alpha1 "github.com/srossross/k8s-test-controller/pkg/apis/tester/v1alpha1" 17 | "k8s.io/apimachinery/pkg/api/errors" 18 | "k8s.io/apimachinery/pkg/labels" 19 | "k8s.io/client-go/tools/cache" 20 | ) 21 | 22 | // TestRunLister helps list TestRuns. 23 | type TestRunLister interface { 24 | // List lists all TestRuns in the indexer. 25 | List(selector labels.Selector) (ret []*v1alpha1.TestRun, err error) 26 | // TestRuns returns an object that can list and get TestRuns. 27 | TestRuns(namespace string) TestRunNamespaceLister 28 | TestRunListerExpansion 29 | } 30 | 31 | // testRunLister implements the TestRunLister interface. 32 | type testRunLister struct { 33 | indexer cache.Indexer 34 | } 35 | 36 | // NewTestRunLister returns a new TestRunLister. 37 | func NewTestRunLister(indexer cache.Indexer) TestRunLister { 38 | return &testRunLister{indexer: indexer} 39 | } 40 | 41 | // List lists all TestRuns in the indexer. 42 | func (s *testRunLister) List(selector labels.Selector) (ret []*v1alpha1.TestRun, err error) { 43 | err = cache.ListAll(s.indexer, selector, func(m interface{}) { 44 | ret = append(ret, m.(*v1alpha1.TestRun)) 45 | }) 46 | return ret, err 47 | } 48 | 49 | // TestRuns returns an object that can list and get TestRuns. 50 | func (s *testRunLister) TestRuns(namespace string) TestRunNamespaceLister { 51 | return testRunNamespaceLister{indexer: s.indexer, namespace: namespace} 52 | } 53 | 54 | // TestRunNamespaceLister helps list and get TestRuns. 55 | type TestRunNamespaceLister interface { 56 | // List lists all TestRuns in the indexer for a given namespace. 57 | List(selector labels.Selector) (ret []*v1alpha1.TestRun, err error) 58 | // Get retrieves the TestRun from the indexer for a given namespace and name. 59 | Get(name string) (*v1alpha1.TestRun, error) 60 | TestRunNamespaceListerExpansion 61 | } 62 | 63 | // testRunNamespaceLister implements the TestRunNamespaceLister 64 | // interface. 65 | type testRunNamespaceLister struct { 66 | indexer cache.Indexer 67 | namespace string 68 | } 69 | 70 | // List lists all TestRuns in the indexer for a given namespace. 71 | func (s testRunNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.TestRun, err error) { 72 | err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { 73 | ret = append(ret, m.(*v1alpha1.TestRun)) 74 | }) 75 | return ret, err 76 | } 77 | 78 | // Get retrieves the TestRun from the indexer for a given namespace and name. 79 | func (s testRunNamespaceLister) Get(name string) (*v1alpha1.TestRun, error) { 80 | obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) 81 | if err != nil { 82 | return nil, err 83 | } 84 | if !exists { 85 | return nil, errors.NewNotFound(v1alpha1.Resource("testrun"), name) 86 | } 87 | return obj.(*v1alpha1.TestRun), nil 88 | } 89 | -------------------------------------------------------------------------------- /pkg/listers/srossross/v1alpha1/testtemplate.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Sean Ross-Ross 6 | 7 | See License in the root of this repo. 8 | 9 | */ 10 | 11 | // This file was automatically generated by lister-gen 12 | 13 | package v1alpha1 14 | 15 | import ( 16 | v1alpha1 "github.com/srossross/k8s-test-controller/pkg/apis/tester/v1alpha1" 17 | "k8s.io/apimachinery/pkg/api/errors" 18 | "k8s.io/apimachinery/pkg/labels" 19 | "k8s.io/client-go/tools/cache" 20 | ) 21 | 22 | // TestTemplateLister helps list TestTemplates. 23 | type TestTemplateLister interface { 24 | // List lists all TestTemplates in the indexer. 25 | List(selector labels.Selector) (ret []*v1alpha1.TestTemplate, err error) 26 | // TestTemplates returns an object that can list and get TestTemplates. 27 | TestTemplates(namespace string) TestTemplateNamespaceLister 28 | TestTemplateListerExpansion 29 | } 30 | 31 | // testTemplateLister implements the TestTemplateLister interface. 32 | type testTemplateLister struct { 33 | indexer cache.Indexer 34 | } 35 | 36 | // NewTestTemplateLister returns a new TestTemplateLister. 37 | func NewTestTemplateLister(indexer cache.Indexer) TestTemplateLister { 38 | return &testTemplateLister{indexer: indexer} 39 | } 40 | 41 | // List lists all TestTemplates in the indexer. 42 | func (s *testTemplateLister) List(selector labels.Selector) (ret []*v1alpha1.TestTemplate, err error) { 43 | err = cache.ListAll(s.indexer, selector, func(m interface{}) { 44 | ret = append(ret, m.(*v1alpha1.TestTemplate)) 45 | }) 46 | return ret, err 47 | } 48 | 49 | // TestTemplates returns an object that can list and get TestTemplates. 50 | func (s *testTemplateLister) TestTemplates(namespace string) TestTemplateNamespaceLister { 51 | return testTemplateNamespaceLister{indexer: s.indexer, namespace: namespace} 52 | } 53 | 54 | // TestTemplateNamespaceLister helps list and get TestTemplates. 55 | type TestTemplateNamespaceLister interface { 56 | // List lists all TestTemplates in the indexer for a given namespace. 57 | List(selector labels.Selector) (ret []*v1alpha1.TestTemplate, err error) 58 | // Get retrieves the TestTemplate from the indexer for a given namespace and name. 59 | Get(name string) (*v1alpha1.TestTemplate, error) 60 | TestTemplateNamespaceListerExpansion 61 | } 62 | 63 | // testTemplateNamespaceLister implements the TestTemplateNamespaceLister 64 | // interface. 65 | type testTemplateNamespaceLister struct { 66 | indexer cache.Indexer 67 | namespace string 68 | } 69 | 70 | // List lists all TestTemplates in the indexer for a given namespace. 71 | func (s testTemplateNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.TestTemplate, err error) { 72 | err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { 73 | ret = append(ret, m.(*v1alpha1.TestTemplate)) 74 | }) 75 | return ret, err 76 | } 77 | 78 | // Get retrieves the TestTemplate from the indexer for a given namespace and name. 79 | func (s testTemplateNamespaceLister) Get(name string) (*v1alpha1.TestTemplate, error) { 80 | obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) 81 | if err != nil { 82 | return nil, err 83 | } 84 | if !exists { 85 | return nil, errors.NewNotFound(v1alpha1.Resource("testtemplate"), name) 86 | } 87 | return obj.(*v1alpha1.TestTemplate), nil 88 | } 89 | -------------------------------------------------------------------------------- /pkg/loop/work.go: -------------------------------------------------------------------------------- 1 | package loop 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "strings" 7 | 8 | corev1 "k8s.io/api/core/v1" 9 | errors "k8s.io/apimachinery/pkg/api/errors" 10 | runtime "k8s.io/apimachinery/pkg/util/runtime" 11 | workqueue "k8s.io/client-go/util/workqueue" 12 | 13 | v1alpha1 "github.com/srossross/k8s-test-controller/pkg/apis/tester/v1alpha1" 14 | controller "github.com/srossross/k8s-test-controller/pkg/controller" 15 | run "github.com/srossross/k8s-test-controller/pkg/run" 16 | ) 17 | 18 | func splitOnce(key, sep string) (string, string) { 19 | tmp := strings.SplitN(key, sep, 2) 20 | if len(tmp) == 1 { 21 | return tmp[0], "" 22 | } 23 | return tmp[0], tmp[1] 24 | } 25 | 26 | func take(ctrl controller.Interface, runner run.Interface, key string) error { 27 | runType, key := splitOnce(key, ":") 28 | 29 | var err error 30 | var testRun *v1alpha1.TestRun 31 | var pod *corev1.Pod 32 | 33 | switch runType { 34 | case run.ReconsilePodStatus: 35 | { 36 | testRun, pod, err = ctrl.GetPodAndTestRunFromKey(key) 37 | if err == nil { 38 | err = runner.PodStateChange(ctrl, testRun, pod) 39 | } 40 | } 41 | case run.ReconsileTestRun: 42 | { 43 | testRun, err = ctrl.GetTestRunFromKey(key) 44 | if err == nil { 45 | err = runner.UpdateTestRun(ctrl, testRun) 46 | } else if errors.IsNotFound(err) { 47 | // FIXME: should this be handled by k8s garbage collection? 48 | err = ctrl.TestRunnerRemovePodsForDeletedTest(key) 49 | } 50 | } 51 | default: 52 | err = fmt.Errorf("key in queue should be of type string but got %T. discarding", key) 53 | } 54 | return err 55 | } 56 | 57 | // Work pops jobs off the queue 58 | func Work(ctrl controller.Interface, runner run.Interface, stopCh chan struct{}, queue workqueue.RateLimitingInterface) { 59 | for { 60 | // we read a message off the queue 61 | key, shutdown := queue.Get() 62 | 63 | // if the queue has been shut down, we should exit the work queue here 64 | if shutdown { 65 | stopCh <- struct{}{} 66 | return 67 | } 68 | 69 | // convert the queue item into a string. If it's not a string, we'll 70 | // simply discard it as invalid data and log a message. 71 | var strKey string 72 | var ok bool 73 | if strKey, ok = key.(string); !ok { 74 | runtime.HandleError(fmt.Errorf("key in queue should be of type string but got %T. discarding", key)) 75 | return 76 | } 77 | 78 | log.Printf("Popped '%s' off the queue", key) 79 | // we define a function here to process a queue item, so that we can 80 | // use 'defer' to make sure the message is marked as Done on the queue 81 | func(key string) { 82 | defer queue.Done(key) 83 | err := take(ctrl, runner, key) 84 | if err != nil { 85 | runtime.HandleError(err) 86 | } 87 | queue.Forget(key) 88 | }(strKey) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /pkg/loop/work_test.go: -------------------------------------------------------------------------------- 1 | package loop 2 | 3 | import ( 4 | "testing" 5 | 6 | fakecontroller "github.com/srossross/k8s-test-controller/pkg/controller/fake" 7 | fakerun "github.com/srossross/k8s-test-controller/pkg/run/fake" 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestTake_BadKey(t *testing.T) { 12 | assert := assert.New(t) 13 | ctrl := fakecontroller.NewController() 14 | runner := fakerun.New() 15 | err := take(ctrl, runner, "BadKey:namespace/name") 16 | assert.NotNil(err) 17 | } 18 | 19 | // func TestTake_ReconsilePodStatus(t *testing.T) { 20 | // assert := assert.New(t) 21 | // ctrl := fakecontroller.NewController() 22 | // runner := fakerun.New() 23 | // err := take(ctrl, runner, "Pod:namespace/name") 24 | // assert.Nil(err) 25 | // } 26 | -------------------------------------------------------------------------------- /pkg/run/TestRunner.go: -------------------------------------------------------------------------------- 1 | package run 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "time" 7 | 8 | v1 "k8s.io/api/core/v1" 9 | errors "k8s.io/apimachinery/pkg/api/errors" 10 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 11 | wait "k8s.io/apimachinery/pkg/util/wait" 12 | 13 | v1alpha1 "github.com/srossross/k8s-test-controller/pkg/apis/tester/v1alpha1" 14 | controller "github.com/srossross/k8s-test-controller/pkg/controller" 15 | ) 16 | 17 | var ( 18 | // APIVersion FIXME: not sure why this is needed 19 | APIVersion = "srossross.github.io/v1alpha1" 20 | 21 | // TestRunKind FIXME: not sure why this is needed 22 | TestRunKind = "TestRun" 23 | ) 24 | 25 | func mergeMaps(a, b map[string]string) map[string]string { 26 | result := make(map[string]string) 27 | for k, v := range a { 28 | result[k] = v 29 | } 30 | for k, v := range b { 31 | result[k] = v 32 | } 33 | return result 34 | } 35 | 36 | func getTestOwnerReference(testRun *v1alpha1.TestRun) metav1.OwnerReference { 37 | Controller := true 38 | return metav1.OwnerReference{ 39 | // FIXME: not sure why testRun.Kind is empty 40 | Kind: TestRunKind, 41 | Name: testRun.Name, 42 | UID: testRun.UID, 43 | // FIXME: not sure why testRun.APIVersion is empty 44 | APIVersion: APIVersion, 45 | Controller: &Controller, 46 | } 47 | } 48 | 49 | // CreateTestPod creates a test pod from a test template 50 | func CreateTestPod(ctrl controller.Interface, testRun *v1alpha1.TestRun, test *v1alpha1.TestTemplate) (*v1.Pod, error) { 51 | 52 | Namespace := testRun.Namespace 53 | if len(Namespace) == 0 { 54 | Namespace = "default" 55 | } 56 | 57 | err := CreateTestRunEventStart(ctrl, testRun, test) 58 | if err != nil { 59 | return nil, err 60 | } 61 | 62 | Annotations := mergeMaps(test.Spec.Template.Annotations, map[string]string{ 63 | "srossross.github.io/v1alpha1": fmt.Sprintf("TestRun:%v/%v", testRun.Namespace, testRun.Name), 64 | }) 65 | Labels := mergeMaps(test.Spec.Template.Labels, map[string]string{ 66 | "test-run": testRun.Name, 67 | "test-name": test.Name, 68 | }) 69 | 70 | pod := &v1.Pod{ 71 | TypeMeta: metav1.TypeMeta{}, 72 | ObjectMeta: metav1.ObjectMeta{ 73 | GenerateName: fmt.Sprintf("%s-", test.Name), 74 | Namespace: Namespace, 75 | Annotations: Annotations, 76 | Labels: Labels, 77 | OwnerReferences: []metav1.OwnerReference{ 78 | getTestOwnerReference(testRun), 79 | }, 80 | }, 81 | Spec: test.Spec.Template.Spec, 82 | Status: v1.PodStatus{}, 83 | } 84 | 85 | createdPod, err := ctrl.CreatePod(Namespace, pod) 86 | if err != nil { 87 | CreateTestRunEvent( 88 | ctrl, testRun, test.Name, "PodCreationFailure", 89 | fmt.Sprintf("Could not create pod for test %s", test.Name), 90 | ) 91 | log.Printf("Error Creating pod while starting test %v", err) 92 | 93 | return nil, err 94 | } 95 | log.Printf(" | Test created pod '%s/%s'", Namespace, createdPod.Name) 96 | 97 | return createdPod, wait.Poll(500*time.Millisecond, 60*time.Second, func() (bool, error) { 98 | 99 | _, err := ctrl.GetPod(testRun.Namespace, createdPod.Name) 100 | 101 | if err == nil { 102 | return true, nil 103 | } 104 | 105 | if errors.IsNotFound(err) { 106 | return false, nil 107 | } 108 | return true, err 109 | }) 110 | } 111 | -------------------------------------------------------------------------------- /pkg/run/crd.go: -------------------------------------------------------------------------------- 1 | package run 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "time" 7 | 8 | apierrors "k8s.io/apimachinery/pkg/api/errors" 9 | errors "k8s.io/apimachinery/pkg/util/errors" 10 | wait "k8s.io/apimachinery/pkg/util/wait" 11 | 12 | apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" 13 | apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" 14 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 15 | 16 | Srossross "github.com/srossross/k8s-test-controller/pkg/apis/tester" 17 | ) 18 | 19 | // TestRunCRDName FIXME: could generate this ? 20 | var TestRunCRDName = "testruns.srossross.github.io" 21 | 22 | // TestTemplateCRDName FIXME: could generate this ? 23 | var TestTemplateCRDName = "testtemplates.srossross.github.io" 24 | 25 | // TestRunCRD exposes the testrun as a crd 26 | var TestRunCRD = &apiextensionsv1beta1.CustomResourceDefinition{ 27 | ObjectMeta: metav1.ObjectMeta{ 28 | Name: TestRunCRDName, 29 | }, 30 | Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{ 31 | Group: Srossross.GroupName, 32 | Version: "v1alpha1", 33 | Scope: apiextensionsv1beta1.NamespaceScoped, 34 | Names: apiextensionsv1beta1.CustomResourceDefinitionNames{ 35 | Plural: "testruns", 36 | Kind: "TestRun", 37 | ShortNames: []string{"tr"}, 38 | }, 39 | }, 40 | } 41 | 42 | // TestTemplateCRD exposes a test as a crd 43 | var TestTemplateCRD = &apiextensionsv1beta1.CustomResourceDefinition{ 44 | ObjectMeta: metav1.ObjectMeta{ 45 | Name: TestTemplateCRDName, 46 | }, 47 | Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{ 48 | Group: Srossross.GroupName, 49 | Version: "v1alpha1", 50 | Scope: apiextensionsv1beta1.NamespaceScoped, 51 | Names: apiextensionsv1beta1.CustomResourceDefinitionNames{ 52 | Plural: "testtemplates", 53 | Kind: "TestTemplate", 54 | ShortNames: []string{"test", "tests"}, 55 | }, 56 | }, 57 | } 58 | 59 | // InstallAllCRDs and wait for them to be ready 60 | func InstallAllCRDs(clientset apiextensionsclient.Interface) error { 61 | var err error 62 | 63 | _, err = InstallCRD(clientset, TestRunCRD) 64 | 65 | if err != nil { 66 | return err 67 | } 68 | 69 | _, err = InstallCRD(clientset, TestTemplateCRD) 70 | 71 | return err 72 | } 73 | 74 | // InstallCRD and wait for it to be ready 75 | func InstallCRD(clientset apiextensionsclient.Interface, crdDef *apiextensionsv1beta1.CustomResourceDefinition) (*apiextensionsv1beta1.CustomResourceDefinition, error) { 76 | 77 | log.Printf("Ensure CRD '%v'", crdDef.Name) 78 | _, err := clientset.ApiextensionsV1beta1().CustomResourceDefinitions().Create(crdDef) 79 | 80 | if err != nil { 81 | if apierrors.IsAlreadyExists(err) { 82 | log.Printf(" CRD '%v' Already exists", crdDef.Name) 83 | return crdDef, nil 84 | } 85 | return nil, err 86 | } 87 | 88 | var crd *apiextensionsv1beta1.CustomResourceDefinition 89 | 90 | log.Printf(" Waiting for '%v' to be Established", crdDef.Name) 91 | err = wait.Poll(500*time.Millisecond, 60*time.Second, func() (bool, error) { 92 | crd, err = clientset.ApiextensionsV1beta1().CustomResourceDefinitions().Get(crdDef.Name, metav1.GetOptions{}) 93 | if err != nil { 94 | return false, err 95 | } 96 | for _, cond := range crd.Status.Conditions { 97 | switch cond.Type { 98 | case apiextensionsv1beta1.Established: 99 | if cond.Status == apiextensionsv1beta1.ConditionTrue { 100 | 101 | return true, err 102 | } 103 | case apiextensionsv1beta1.NamesAccepted: 104 | if cond.Status == apiextensionsv1beta1.ConditionFalse { 105 | fmt.Printf("Name conflict: %v\n", cond.Reason) 106 | } 107 | } 108 | } 109 | return false, err 110 | }) 111 | if err != nil { 112 | deleteErr := clientset.ApiextensionsV1beta1().CustomResourceDefinitions().Delete(crdDef.Name, nil) 113 | if deleteErr != nil { 114 | return nil, errors.NewAggregate([]error{err, deleteErr}) 115 | } 116 | return nil, err 117 | } 118 | log.Printf(" CRD '%v' Ready", crdDef.Name) 119 | return crd, nil 120 | } 121 | -------------------------------------------------------------------------------- /pkg/run/events.go: -------------------------------------------------------------------------------- 1 | package run 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | "time" 8 | 9 | v1alpha1 "github.com/srossross/k8s-test-controller/pkg/apis/tester/v1alpha1" 10 | controller "github.com/srossross/k8s-test-controller/pkg/controller" 11 | v1 "k8s.io/api/core/v1" 12 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 13 | ) 14 | 15 | // CreateTestRunEvent creates an event 16 | func CreateTestRunEvent( 17 | ctrl controller.Interface, 18 | testRun *v1alpha1.TestRun, 19 | testName string, 20 | Reason string, 21 | Message string, 22 | ) error { 23 | 24 | objectReference := v1.ObjectReference{ 25 | // FIXME: not sure why testRun.Kind is empty 26 | Kind: "TestRun", 27 | Namespace: testRun.Namespace, 28 | Name: testRun.Name, 29 | UID: testRun.UID, 30 | // FIXME: not sure why testRun.APIVersion is empty 31 | APIVersion: APIVersion, 32 | ResourceVersion: testRun.ResourceVersion, 33 | } 34 | 35 | hostname, err := os.Hostname() 36 | if err != nil { 37 | hostname = "hostname" 38 | } 39 | now := metav1.Time{Time: time.Now()} 40 | event := &v1.Event{ 41 | TypeMeta: metav1.TypeMeta{}, 42 | ObjectMeta: metav1.ObjectMeta{ 43 | GenerateName: "test-run-event", 44 | Labels: map[string]string{ 45 | "test-run": testRun.Name, 46 | "test-name": testName, 47 | }, 48 | }, 49 | InvolvedObject: objectReference, 50 | 51 | // Machine Reason 52 | Reason: Reason, 53 | // User readable reason 54 | Message: Message, 55 | 56 | // FIXME: populate with real values 57 | Source: v1.EventSource{ 58 | Component: "test-controller", 59 | Host: hostname, 60 | }, 61 | FirstTimestamp: now, 62 | LastTimestamp: now, 63 | Count: 1, 64 | Type: "Normal", 65 | } 66 | 67 | _, err = ctrl.CoreV1().Events(testRun.Namespace).Create(event) 68 | 69 | if err != nil { 70 | log.Printf("Error Creating event while starting test %v", err) 71 | return err 72 | } 73 | 74 | return nil 75 | 76 | } 77 | 78 | // CreateTestRunEventStart will create a k8s event when a test pod is created 79 | func CreateTestRunEventStart(ctrl controller.Interface, testRun *v1alpha1.TestRun, test *v1alpha1.TestTemplate) error { 80 | return CreateTestRunEvent( 81 | ctrl, testRun, test.Name, 82 | "TestStart", 83 | fmt.Sprintf("Starting test %s", test.Name), 84 | ) 85 | } 86 | -------------------------------------------------------------------------------- /pkg/run/fake/fake.go: -------------------------------------------------------------------------------- 1 | package run 2 | 3 | import ( 4 | corev1 "k8s.io/api/core/v1" 5 | 6 | v1alpha1 "github.com/srossross/k8s-test-controller/pkg/apis/tester/v1alpha1" 7 | controller "github.com/srossross/k8s-test-controller/pkg/controller" 8 | run "github.com/srossross/k8s-test-controller/pkg/run" 9 | ) 10 | 11 | type fakeRunner struct { 12 | } 13 | 14 | func (f *fakeRunner) PodStateChange(ctrl controller.Interface, testRun *v1alpha1.TestRun, pod *corev1.Pod) error { 15 | return nil 16 | } 17 | 18 | func (f *fakeRunner) UpdateTestRun(ctrl controller.Interface, testRun *v1alpha1.TestRun) error { 19 | return nil 20 | } 21 | 22 | // New creates a fake runner 23 | func New() run.Interface { 24 | return &fakeRunner{} 25 | } 26 | -------------------------------------------------------------------------------- /pkg/run/informers.go: -------------------------------------------------------------------------------- 1 | package run 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | 7 | log "github.com/golang/glog" 8 | 9 | v1alpha1 "github.com/srossross/k8s-test-controller/pkg/apis/tester/v1alpha1" 10 | factory "github.com/srossross/k8s-test-controller/pkg/informers/externalversions" 11 | "k8s.io/api/core/v1" 12 | cache "k8s.io/client-go/tools/cache" 13 | workqueue "k8s.io/client-go/util/workqueue" 14 | ) 15 | 16 | // ReconsileType is a string 17 | type ReconsileType string 18 | 19 | var ( 20 | // ReconsilePodStatus tells the Reconcile loop that a pod status has changed 21 | ReconsilePodStatus = "Pod" 22 | // ReconsileTestRun tells the Reconcile loop that a test run has changed 23 | ReconsileTestRun = "TestRun" 24 | ) 25 | 26 | func isStatusChange(old, cur interface{}) bool { 27 | oldPod, ok := old.(*v1.Pod) 28 | if !ok { 29 | return false 30 | } 31 | curPod, ok := cur.(*v1.Pod) 32 | if !ok { 33 | return false 34 | } 35 | 36 | if oldPod.Status.Phase != curPod.Status.Phase { 37 | log.Infof( 38 | "Pod '%v/%v' phase changed from '%s' to '%s'", 39 | curPod.Namespace, curPod.Name, 40 | oldPod.Status.Phase, curPod.Status.Phase, 41 | ) 42 | return true 43 | } 44 | return false 45 | } 46 | 47 | func testRunKey(cur interface{}) (string, bool) { 48 | 49 | testRun, ok := cur.(*v1alpha1.TestRun) 50 | 51 | if !ok { 52 | return "", false 53 | } 54 | return fmt.Sprintf("%v:%v/%v", ReconsileTestRun, testRun.Namespace, testRun.Name), true 55 | } 56 | 57 | func podTestRunKey(cur interface{}) (string, bool) { 58 | pod, ok := cur.(*v1.Pod) 59 | if !ok { 60 | return "", false 61 | } 62 | annotaion, ok := pod.Annotations["srossross.github.io/v1alpha1"] 63 | if !ok { 64 | return "", false 65 | } 66 | return annotaion, true 67 | } 68 | 69 | // NewTestRunInformer creates a new test run Informer that watches and caches testruns 70 | func NewTestRunInformer( 71 | sharedFactory factory.SharedInformerFactory, 72 | queue workqueue.RateLimitingInterface, 73 | ) cache.SharedIndexInformer { 74 | 75 | runInformer := sharedFactory.Srossross().V1alpha1().TestRuns().Informer() 76 | // we add a new event handler, watching for changes to API resources. 77 | 78 | enqueue := func(cur interface{}) { 79 | key, ok := testRunKey(cur) 80 | if !ok { 81 | log.Infof("Error getting testrun queue key") 82 | return 83 | } 84 | queue.Add(key) 85 | } 86 | 87 | runInformer.AddEventHandler( 88 | cache.ResourceEventHandlerFuncs{ 89 | AddFunc: func(cur interface{}) { enqueue(cur) }, 90 | UpdateFunc: func(old, cur interface{}) { 91 | if !reflect.DeepEqual(old, cur) { 92 | enqueue(cur) 93 | } 94 | }, 95 | DeleteFunc: func(cur interface{}) { enqueue(cur) }, 96 | }, 97 | ) 98 | 99 | return runInformer 100 | } 101 | 102 | // NewTestInformer creates a new test Informer that watches and caches tests 103 | func NewTestInformer(sharedFactory factory.SharedInformerFactory, queue workqueue.RateLimitingInterface) cache.SharedIndexInformer { 104 | testInformer := sharedFactory.Srossross().V1alpha1().TestTemplates().Informer() 105 | // we add a new event handler, watching for changes to API resources. 106 | testInformer.AddEventHandler( 107 | cache.ResourceEventHandlerFuncs{ 108 | AddFunc: func(cur interface{}) { 109 | key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(cur) 110 | if err != nil { 111 | log.Fatalf("Error in DeletionHandlingMetaNamespaceKeyFunc %v", err.Error()) 112 | } 113 | log.V(4).Infof("Test %v Added (not triggering reconsile loop)", key) 114 | }, 115 | UpdateFunc: func(old, cur interface{}) { 116 | key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(cur) 117 | if err != nil { 118 | log.Fatalf("Error in DeletionHandlingMetaNamespaceKeyFunc %v", err.Error()) 119 | } 120 | log.V(4).Infof("Test %v Updated (not triggering reconsile loop)", key) 121 | }, 122 | DeleteFunc: func(cur interface{}) { 123 | key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(cur) 124 | if err != nil { 125 | log.Fatalf("Error in DeletionHandlingMetaNamespaceKeyFunc %v", err.Error()) 126 | } 127 | log.V(4).Infof("Test %v Deleted (not triggering reconsile loop)", key) 128 | }, 129 | }, 130 | ) 131 | return testInformer 132 | } 133 | 134 | // SetupPodInformer creates a new test Informer that watches and caches pods 135 | func SetupPodInformer(podInformer cache.SharedIndexInformer, queue workqueue.RateLimitingInterface) cache.SharedIndexInformer { 136 | 137 | enqueue := func(cur interface{}) { 138 | key, ok := podTestRunKey(cur) 139 | if !ok { 140 | // log.Infof("Error getting testrun queue key") 141 | return 142 | } 143 | queue.Add(key) 144 | } 145 | 146 | enqueuePodStatEvent := func(cur interface{}) { 147 | _, ok := podTestRunKey(cur) 148 | if !ok { 149 | return 150 | } 151 | key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(cur) 152 | if err != nil { 153 | return 154 | } 155 | queue.Add(fmt.Sprintf("%s:%s", ReconsilePodStatus, key)) 156 | } 157 | 158 | podInformer.AddEventHandler( 159 | cache.ResourceEventHandlerFuncs{ 160 | AddFunc: func(cur interface{}) { enqueue(cur) }, 161 | UpdateFunc: func(old, cur interface{}) { 162 | if !reflect.DeepEqual(old, cur) { 163 | // FIXME: we should detect a change in state so that 164 | // we can add an test fail/success event 165 | if isStatusChange(old, cur) { 166 | enqueuePodStatEvent(cur) 167 | } 168 | enqueue(cur) 169 | } 170 | }, 171 | DeleteFunc: func(cur interface{}) { enqueue(cur) }, 172 | }, 173 | ) 174 | return podInformer 175 | } 176 | -------------------------------------------------------------------------------- /pkg/run/podutils.go: -------------------------------------------------------------------------------- 1 | package run 2 | 3 | import ( 4 | // "flag" 5 | // "fmt" 6 | // "log" 7 | "fmt" 8 | 9 | corev1 "k8s.io/api/core/v1" 10 | 11 | v1alpha1 "github.com/srossross/k8s-test-controller/pkg/apis/tester/v1alpha1" 12 | controller "github.com/srossross/k8s-test-controller/pkg/controller" 13 | ) 14 | 15 | // PodStateChange records an event for a test state change 16 | func (r *runner) PodStateChange(ctrl controller.Interface, testRun *v1alpha1.TestRun, pod *corev1.Pod) error { 17 | testName, ok := pod.Labels["test-name"] 18 | if !ok { 19 | return fmt.Errorf("Could not get test-name label from pod %s", pod.Name) 20 | } 21 | var Reason string 22 | switch pod.Status.Phase { 23 | case "Succeeded": 24 | Reason = "TestSuccess" 25 | case "Failed": 26 | Reason = "TestFail" 27 | case "Unknown": 28 | Reason = "TestError" 29 | case "Pending": 30 | return nil 31 | case "Running": 32 | return nil 33 | } 34 | 35 | return CreateTestRunEvent( 36 | ctrl, testRun, testName, 37 | Reason, 38 | fmt.Sprintf("Test pod '%s' exited with status '%s'", pod.Name, pod.Status.Phase), 39 | ) 40 | 41 | } 42 | -------------------------------------------------------------------------------- /pkg/run/reconcile.go: -------------------------------------------------------------------------------- 1 | package run 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "time" 7 | 8 | corev1 "k8s.io/api/core/v1" 9 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 10 | labels "k8s.io/apimachinery/pkg/labels" 11 | "k8s.io/apimachinery/pkg/util/wait" 12 | 13 | v1alpha1 "github.com/srossross/k8s-test-controller/pkg/apis/tester/v1alpha1" 14 | controller "github.com/srossross/k8s-test-controller/pkg/controller" 15 | ) 16 | 17 | func getTestsForTestRun(ctrl controller.Interface, testRun *v1alpha1.TestRun) ([]*v1alpha1.TestTemplate, error) { 18 | selector, err := metav1.LabelSelectorAsSelector(testRun.Spec.Selector) 19 | if selector.String() == "" { 20 | selector = labels.Everything() 21 | } 22 | 23 | if err != nil { 24 | return nil, err 25 | } 26 | 27 | return ctrl.TestTemplateLister().TestTemplates(testRun.Namespace).List(selector) 28 | } 29 | 30 | func initializeStatus(ctrl controller.Interface, testRun *v1alpha1.TestRun) error { 31 | tests, err := getTestsForTestRun(ctrl, testRun) 32 | 33 | if err != nil { 34 | return fmt.Errorf("error getting list of tests: %s", err.Error()) 35 | } 36 | 37 | log.Printf("testRun.Status.Status is '%v'", testRun.Status.Status) 38 | testRunCopy := testRun.DeepCopy() 39 | 40 | testRecords := []v1alpha1.TestRunRecord{} 41 | for _, test := range tests { 42 | testRecords = append(testRecords, v1alpha1.TestRunRecord{ 43 | TestName: test.Name, 44 | PodRef: nil, 45 | StartTime: nil, 46 | EndTime: nil, 47 | Result: "N/A", 48 | }) 49 | } 50 | 51 | testRunCopy.Status.Status = v1alpha1.TestRunRunning 52 | testRunCopy.Status.Records = testRecords 53 | 54 | log.Printf("Initialize '%v/%v'", testRun.Namespace, testRun.Name) 55 | if _, err = ctrl.SrossrossV1alpha1().TestRuns(testRun.Namespace).Update(testRunCopy); err != nil { 56 | return err 57 | } 58 | 59 | return wait.Poll(500*time.Millisecond, 60*time.Second, func() (bool, error) { 60 | t, err := ctrl.TestRunLister().TestRuns(testRun.Namespace).Get(testRun.Name) 61 | if err != nil { 62 | return true, err 63 | } 64 | if t.Status.Records != nil { 65 | return true, nil 66 | } 67 | return false, nil 68 | }) 69 | 70 | } 71 | 72 | type runStats struct { 73 | CompletedCount int 74 | FailCount int 75 | } 76 | 77 | func testFinished(ctrl controller.Interface, testRun *v1alpha1.TestRun, i int, Result string) error { 78 | testRun = testRun.DeepCopy() 79 | 80 | testRun.Status.Records[i].EndTime = &metav1.Time{Time: time.Now()} 81 | if testRun.Status.Records[i].StartTime == nil { 82 | testRun.Status.Records[i].StartTime = testRun.Status.Records[i].EndTime 83 | } 84 | testRun.Status.Records[i].Result = Result 85 | 86 | if _, err := ctrl.SrossrossV1alpha1().TestRuns(testRun.Namespace).Update(testRun); err != nil { 87 | return err 88 | } 89 | 90 | return wait.Poll(500*time.Millisecond, 60*time.Second, func() (bool, error) { 91 | t, err := ctrl.TestRunLister().TestRuns(testRun.Namespace).Get(testRun.Name) 92 | if err != nil { 93 | return true, err 94 | } 95 | if t.Status.Records[i].EndTime != nil { 96 | return true, nil 97 | } 98 | return false, nil 99 | }) 100 | 101 | } 102 | 103 | func testStarted(ctrl controller.Interface, testRun *v1alpha1.TestRun, i int, pod *corev1.Pod) error { 104 | testRun = testRun.DeepCopy() 105 | 106 | testRun.Status.Records[i].StartTime = &metav1.Time{Time: time.Now()} 107 | testRun.Status.Records[i].PodRef = &corev1.ObjectReference{ 108 | Kind: "Pod", 109 | Namespace: pod.Namespace, 110 | Name: pod.Name, 111 | APIVersion: pod.APIVersion, 112 | ResourceVersion: pod.ResourceVersion, 113 | } 114 | 115 | if _, err := ctrl.SrossrossV1alpha1().TestRuns(testRun.Namespace).Update(testRun); err != nil { 116 | return err 117 | } 118 | 119 | return wait.Poll(500*time.Millisecond, 60*time.Second, func() (bool, error) { 120 | t, err := ctrl.TestRunLister().TestRuns(testRun.Namespace).Get(testRun.Name) 121 | if err != nil { 122 | return true, err 123 | } 124 | if t.Status.Records[i].StartTime != nil { 125 | return true, nil 126 | } 127 | return false, nil 128 | }) 129 | } 130 | 131 | func runNextNTests(ctrl controller.Interface, testRun *v1alpha1.TestRun, tests []*v1alpha1.TestTemplate, JobsSlots int) error { 132 | podMap, err := createPodMap(ctrl, testRun) 133 | if err != nil { 134 | return err 135 | } 136 | 137 | testMap := createTestMap(tests) 138 | 139 | for i, record := range testRun.Status.Records { 140 | if JobsSlots <= 0 { 141 | log.Printf(" | No more jobs allowed (maxjobs: %v). Will wait for next event", getJobSlots(testRun)) 142 | return nil 143 | } 144 | 145 | if record.EndTime != nil { 146 | // The Test has ended 147 | continue 148 | } 149 | if record.StartTime == nil { 150 | // The Test not started 151 | test, ok := testMap[record.TestName] 152 | if !ok { 153 | err = testFinished(ctrl, testRun, i, "TestRemoved") 154 | return err 155 | } 156 | 157 | var pod *corev1.Pod 158 | pod, err = CreateTestPod(ctrl, testRun, test) 159 | 160 | if err != nil { 161 | return testFinished(ctrl, testRun, i, "PodStartError") 162 | } 163 | return testStarted(ctrl, testRun, i, pod) 164 | } 165 | 166 | // record.StartTime is non nil 167 | 168 | if pod, ok := podMap[record.TestName]; ok { 169 | log.Printf(" | - Pod '%v' exists - Status: %v", pod.Name, pod.Status.Phase) 170 | switch pod.Status.Phase { 171 | case corev1.PodSucceeded: 172 | return testFinished(ctrl, testRun, i, string(pod.Status.Phase)) 173 | case corev1.PodFailed: 174 | return testFinished(ctrl, testRun, i, string(pod.Status.Phase)) 175 | case corev1.PodUnknown: 176 | return testFinished(ctrl, testRun, i, string(pod.Status.Phase)) 177 | // These are running and taking up a job slot! 178 | case corev1.PodPending: 179 | JobsSlots-- 180 | continue 181 | case corev1.PodRunning: 182 | JobsSlots-- 183 | continue 184 | } 185 | } 186 | } 187 | // Only get here if all jobs finish 188 | return nil 189 | } 190 | 191 | func createPodMap(ctrl controller.Interface, testRun *v1alpha1.TestRun) (map[string]*corev1.Pod, error) { 192 | pods, err := ctrl.ListPods(testRun.Namespace, labels.Everything()) 193 | podMap := make(map[string]*corev1.Pod) 194 | if err != nil { 195 | return podMap, fmt.Errorf("Error getting list of pods: %s", err.Error()) 196 | } 197 | 198 | pods = controller.TestRunFilter(pods, testRun.Name) 199 | 200 | log.Printf(" | Total Pod Count: %v", len(pods)) 201 | 202 | for _, pod := range pods { 203 | // log.Printf(" | Pod: %v", pod.Name) 204 | podMap[pod.Labels["test-name"]] = pod 205 | } 206 | 207 | return podMap, nil 208 | 209 | } 210 | 211 | func createTestMap(tests []*v1alpha1.TestTemplate) map[string]*v1alpha1.TestTemplate { 212 | testMap := make(map[string]*v1alpha1.TestTemplate) 213 | 214 | for _, test := range tests { 215 | testMap[test.Name] = test 216 | } 217 | return testMap 218 | } 219 | 220 | func getJobSlots(testRun *v1alpha1.TestRun) int { 221 | // FIXME: should be a default in the schema ... 222 | if testRun.Spec.MaxJobs > 0 { 223 | return testRun.Spec.MaxJobs 224 | } 225 | return 1 226 | } 227 | 228 | func testRunComplete(ctrl controller.Interface, testRun *v1alpha1.TestRun, stats runStats) error { 229 | Message := fmt.Sprintf("Ran %v tests, %v failures", stats.CompletedCount, stats.FailCount) 230 | var Reason string 231 | testRun = testRun.DeepCopy() 232 | 233 | testRun.Status.Status = v1alpha1.TestRunComplete 234 | testRun.Status.Success = stats.FailCount == 0 235 | testRun.Status.Message = Message 236 | 237 | log.Printf("Saving '%v/%v'", testRun.Namespace, testRun.Name) 238 | if _, err := ctrl.SrossrossV1alpha1().TestRuns(testRun.Namespace).Update(testRun); err != nil { 239 | return err 240 | } 241 | log.Printf("We are done here %v tests completed", stats.CompletedCount) 242 | 243 | switch stats.FailCount == 0 { 244 | case true: 245 | Reason = "TestRunSuccess" 246 | case false: 247 | Reason = "TestRunFail" 248 | } 249 | return CreateTestRunEvent(ctrl, testRun, "", Reason, Message) 250 | } 251 | 252 | // UpdateTestRun will Reconcile a single test run 253 | func (r *runner) UpdateTestRun(ctrl controller.Interface, testRun *v1alpha1.TestRun) error { 254 | 255 | if testRun.Status.Status == v1alpha1.TestRunComplete { 256 | log.Printf(" | '%v/%v' is already Complete - Skipping", testRun.Namespace, testRun.Name) 257 | return nil 258 | } 259 | 260 | if testRun.Status.Status == "" { 261 | err := initializeStatus(ctrl, testRun) 262 | if err != nil { 263 | return err 264 | } 265 | } 266 | 267 | stats := runStats{0, 0} 268 | for _, record := range testRun.Status.Records { 269 | if record.EndTime != nil { 270 | stats.CompletedCount++ 271 | if record.Result != string(corev1.PodSucceeded) { 272 | stats.FailCount++ 273 | } 274 | } 275 | } 276 | if stats.CompletedCount == len(testRun.Status.Records) { 277 | return testRunComplete(ctrl, testRun, stats) 278 | } 279 | 280 | log.Printf("Running '%v/%v'", testRun.Namespace, testRun.Name) 281 | 282 | log.Printf(" | %v/%v", testRun.Namespace, testRun.Name) 283 | 284 | tests, err := getTestsForTestRun(ctrl, testRun) 285 | 286 | if err != nil { 287 | return fmt.Errorf("error getting list of tests: %s", err.Error()) 288 | } 289 | log.Printf(" | Test Count: %v", len(tests)) 290 | 291 | JobsSlots := getJobSlots(testRun) 292 | 293 | return runNextNTests(ctrl, testRun, tests, JobsSlots) 294 | } 295 | -------------------------------------------------------------------------------- /pkg/run/runner.go: -------------------------------------------------------------------------------- 1 | package run 2 | 3 | import ( 4 | corev1 "k8s.io/api/core/v1" 5 | 6 | v1alpha1 "github.com/srossross/k8s-test-controller/pkg/apis/tester/v1alpha1" 7 | controller "github.com/srossross/k8s-test-controller/pkg/controller" 8 | ) 9 | 10 | // Interface to be passed into work function 11 | type Interface interface { 12 | PodStateChange(ctrl controller.Interface, testRun *v1alpha1.TestRun, pod *corev1.Pod) error 13 | UpdateTestRun(ctrl controller.Interface, testRun *v1alpha1.TestRun) error 14 | } 15 | 16 | type runner struct { 17 | } 18 | 19 | // New creates a new runner 20 | func New() Interface { 21 | return &runner{} 22 | } 23 | --------------------------------------------------------------------------------