├── .circleci └── config.yml ├── .dockerignore ├── .gcloudignore ├── .gitignore ├── Makefile ├── README.md ├── cloudbuild.yaml ├── cmd ├── transformation-adapter │ ├── Dockerfile │ ├── kodata │ │ ├── HEAD │ │ └── refs │ └── main.go └── transformation-controller │ ├── Dockerfile │ ├── kodata │ ├── HEAD │ └── refs │ └── main.go ├── config ├── 100-namespace.yaml ├── 200-controller-clusterrole.yaml ├── 200-serviceaccount.yaml ├── 200-user-clusterrole.yaml ├── 201-clusterrolebinding.yaml ├── 300-transformation.yaml ├── 500-controller.yaml ├── config-leader-election.yaml ├── config-logging.yaml ├── config-observability.yaml └── samples │ ├── broker.yaml │ ├── multi-target │ ├── github-transformation.yaml │ ├── githubsource.yaml │ ├── googlesheet-target.yaml │ └── slack-target.yaml │ └── simple-event │ ├── event-display.yaml │ ├── pingsource.yaml │ ├── transformation.yaml │ └── triggers.yaml ├── go.mod ├── go.sum ├── hack ├── boilerplate │ └── boilerplate.go.txt ├── inc.Codegen.mk └── release-notes.sh ├── kustomization.yaml ├── licenses ├── EULA.pdf └── LICENSE └── pkg ├── apis └── transformation │ ├── register.go │ └── v1alpha1 │ ├── deepcopy_generated.go │ ├── doc.go │ ├── register.go │ ├── register_test.go │ ├── transformation_defaults.go │ ├── transformation_lifecycle.go │ ├── transformation_types.go │ └── transformation_validation.go ├── client └── generated │ ├── clientset │ └── internalclientset │ │ ├── clientset.go │ │ ├── doc.go │ │ ├── fake │ │ ├── clientset_generated.go │ │ ├── doc.go │ │ └── register.go │ │ ├── scheme │ │ ├── doc.go │ │ └── register.go │ │ └── typed │ │ └── transformation │ │ └── v1alpha1 │ │ ├── doc.go │ │ ├── fake │ │ ├── doc.go │ │ ├── fake_transformation.go │ │ └── fake_transformation_client.go │ │ ├── generated_expansion.go │ │ ├── transformation.go │ │ └── transformation_client.go │ ├── informers │ └── externalversions │ │ ├── factory.go │ │ ├── generic.go │ │ ├── internalinterfaces │ │ └── factory_interfaces.go │ │ └── transformation │ │ ├── interface.go │ │ └── v1alpha1 │ │ ├── interface.go │ │ └── transformation.go │ ├── injection │ ├── client │ │ ├── client.go │ │ └── fake │ │ │ └── fake.go │ ├── informers │ │ ├── factory │ │ │ ├── factory.go │ │ │ ├── fake │ │ │ │ └── fake.go │ │ │ └── filtered │ │ │ │ ├── fake │ │ │ │ └── fake_filtered_factory.go │ │ │ │ └── filtered_factory.go │ │ └── transformation │ │ │ └── v1alpha1 │ │ │ └── transformation │ │ │ ├── fake │ │ │ └── fake.go │ │ │ ├── filtered │ │ │ ├── fake │ │ │ │ └── fake.go │ │ │ └── transformation.go │ │ │ └── transformation.go │ └── reconciler │ │ └── transformation │ │ └── v1alpha1 │ │ └── transformation │ │ ├── controller.go │ │ ├── reconciler.go │ │ └── state.go │ └── listers │ └── transformation │ └── v1alpha1 │ ├── expansion_generated.go │ └── transformation.go ├── pipeline ├── common │ ├── convert │ │ ├── convert.go │ │ └── convert_test.go │ └── storage │ │ └── storage.go ├── handler.go ├── handler_test.go ├── pipeline.go └── transformer │ ├── add │ └── add.go │ ├── delete │ └── delete.go │ ├── shift │ └── shift.go │ ├── store │ └── store.go │ └── transformer.go └── reconciler └── controller ├── controller.go ├── resources └── knservice.go └── transformation.go /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | orbs: 4 | aws-eks: circleci/aws-eks@1 5 | gcp-cli: circleci/gcp-cli@1 6 | go: circleci/go@1 7 | 8 | jobs: 9 | checkout: 10 | executor: 11 | name: go/default 12 | tag: '1.15' 13 | steps: 14 | - checkout 15 | - go/mod-download-cached 16 | - persist_to_workspace: 17 | root: ~/ 18 | paths: 19 | - go 20 | - project 21 | 22 | build: 23 | executor: 24 | name: go/default 25 | tag: '1.15' 26 | steps: 27 | - attach_workspace: 28 | at: ~/ 29 | - run: 30 | name: Building package 31 | command: make build 32 | 33 | test: 34 | executor: 35 | name: go/default 36 | tag: '1.15' 37 | steps: 38 | - attach_workspace: 39 | at: ~/ 40 | - run: 41 | name: Run fmt-test 42 | command: make fmt-test 43 | - run: 44 | name: Installing golangci-lint 45 | command: make install-golangci-lint 46 | - run: 47 | name: Run lint 48 | command: make lint 49 | - run: 50 | name: Run test/cover 51 | command: make cover 52 | environment: 53 | TEST_OUTPUT_DIR: /tmp/test-results/ 54 | COVER_OUTPUT_DIR: /tmp/cover-results/ 55 | - store_test_results: 56 | path: /tmp/test-results/ 57 | - store_artifacts: 58 | path: /tmp/cover-results/ 59 | 60 | publish: 61 | executor: 62 | name: gcp-cli/google 63 | steps: 64 | - attach_workspace: 65 | at: ~/ 66 | - gcp-cli/initialize 67 | - run: 68 | name: Publishing docker image 69 | command: IMAGE_SHA=${CIRCLE_SHA1} IMAGE_TAG=${CIRCLE_TAG:-latest} make -j4 cloudbuild 70 | 71 | deploy: 72 | description: Patches target cluster configuration 73 | executor: 74 | name: go/default 75 | tag: '1.15' 76 | parameters: 77 | cluster: 78 | type: string 79 | committer_name: 80 | type: string 81 | default: TriggerMesh Bot 82 | committer_email: 83 | type: string 84 | default: bot@triggermesh.com 85 | steps: 86 | - attach_workspace: 87 | at: ~/ 88 | - add_ssh_keys 89 | - run: ssh-keyscan github.com >> ~/.ssh/known_hosts 90 | - run: 91 | name: Configuring git 92 | command: | 93 | git config --global user.name '<< parameters.committer_name >>' 94 | git config --global user.email '<< parameters.committer_email >>' 95 | - run: 96 | name: Cloning config repository 97 | command: git clone --single-branch git@github.com:triggermesh/config.git tmconfig 98 | - run: 99 | name: Updating overlays/<< parameters.cluster >>/transformation manifests 100 | working_directory: tmconfig/ 101 | command: | 102 | for cmd in $(sed -n -e 's/^COMMANDS[[:space:]]*=[[:space:]]*\(.*\)$/\1/p' ~/project/Makefile); do 103 | sed -i overlays/<< parameters.cluster >>/transformation/deployment.yaml \ 104 | -e "s|\(gcr.io/triggermesh/${cmd}:\).*|\1${CIRCLE_TAG:-${CIRCLE_SHA1}}|g" 105 | done 106 | 107 | sed -i overlays/<< parameters.cluster >>/transformation/kustomization.yaml \ 108 | -e "s|github.com/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}\(?ref=.*\)\?|github.com/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}?ref=${CIRCLE_TAG:-${CIRCLE_SHA1}}|g" 109 | 110 | git --no-pager diff 111 | - run: 112 | name: Committing overlays/<< parameters.cluster >>/transformation updates 113 | working_directory: tmconfig/ 114 | command: | 115 | git add overlays 116 | git commit -m "Update overlays/<< parameters.cluster >>/transformation deployments to '${CIRCLE_TAG:-${CIRCLE_SHA1}}'" 117 | git push origin main 118 | 119 | release: 120 | executor: 121 | name: go/default 122 | tag: '1.15' 123 | steps: 124 | - attach_workspace: 125 | at: ~/ 126 | - aws-eks/update-kubeconfig-with-authenticator: 127 | cluster-name: $AWS_CLUSTER_NAME 128 | aws-region: $AWS_REGION 129 | install-kubectl: true 130 | - run: 131 | name: Building release packages 132 | command: IMAGE_TAG=${CIRCLE_TAG:-latest} make release 133 | environment: 134 | DIST_DIR: /tmp/dist/ 135 | - run: 136 | name: Installing github-release tool 137 | command: go get github.com/meterup/github-release 138 | - run: 139 | name: Creating github release 140 | command: | 141 | PRE_RELEASE=${CIRCLE_TAG/${CIRCLE_TAG%-rc[0-9]*}/} 142 | github-release delete -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} -t ${CIRCLE_TAG} 2>/dev/null ||: 143 | ./hack/release-notes.sh ${CIRCLE_TAG} | github-release release ${PRE_RELEASE:+-p} -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} -t ${CIRCLE_TAG} -d - 144 | for f in $(find /tmp/dist -type f); do github-release upload -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} -t ${CIRCLE_TAG} -n $(basename ${f}) -f ${f} ; done 145 | 146 | workflows: 147 | build-test-and-publish: 148 | jobs: 149 | - checkout: 150 | filters: 151 | tags: 152 | only: /^v([0-9]+)\.([0-9]+)\.([0-9]+)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+)?$/ 153 | - build: 154 | context: production 155 | requires: 156 | - checkout 157 | filters: 158 | tags: 159 | only: /^v([0-9]+)\.([0-9]+)\.([0-9]+)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+)?$/ 160 | - test: 161 | requires: 162 | - checkout 163 | filters: 164 | tags: 165 | only: /^v([0-9]+)\.([0-9]+)\.([0-9]+)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+)?$/ 166 | - publish: 167 | context: production 168 | requires: 169 | - build 170 | - test 171 | filters: 172 | tags: 173 | only: /^v([0-9]+)\.([0-9]+)\.([0-9]+)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+)?$/ 174 | branches: 175 | only: main 176 | - deploy: 177 | name: update-staging-config 178 | cluster: staging 179 | requires: 180 | - publish 181 | filters: 182 | branches: 183 | only: main 184 | tags: 185 | only: /^v([0-9]+)\.([0-9]+)\.([0-9]+)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+)?$/ 186 | - deploy: 187 | name: update-production-config 188 | cluster: prod 189 | requires: 190 | - update-staging-config 191 | filters: 192 | tags: 193 | only: /^v([0-9]+)\.([0-9]+)\.([0-9]+)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+)?$/ 194 | branches: 195 | ignore: /.*/ 196 | - release: 197 | context: production 198 | requires: 199 | - publish 200 | filters: 201 | tags: 202 | only: /^v([0-9]+)\.([0-9]+)\.([0-9]+)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+)?$/ 203 | branches: 204 | ignore: /.*/ 205 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | # Top level files and directories 2 | /.circleci/ 3 | /.git/ 4 | /test/ 5 | /hack/ 6 | !/hack/inc.Codegen.mk 7 | /.dockerignore 8 | /.gcloudignore 9 | /cloudbuild.yaml 10 | 11 | # Files copied to KO_DATA_PATH directory 12 | !/.git/HEAD 13 | !/.git/refs/ 14 | 15 | # Patterns 16 | **/config/ 17 | **/*.md 18 | **/*_test.go 19 | **/.gitignore 20 | **/Dockerfile 21 | 22 | # Binaries 23 | **/_output/ 24 | -------------------------------------------------------------------------------- /.gcloudignore: -------------------------------------------------------------------------------- 1 | #!include:.dockerignore 2 | !Dockerfile 3 | 4 | # Explicit inclusions are handled differently from .dockerignore files. 5 | # Ref. https://stackoverflow.com/a/29932318 6 | !/.git/ 7 | /.git/* 8 | !/.git/HEAD 9 | !.git/refs/ 10 | !/hack/ 11 | /hack/* 12 | !/hack/inc.Codegen.mk 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Local configurations and secrets 2 | /.env/ 3 | /.local/ 4 | 5 | # Continuous integration 6 | 7 | # Editors and IDEs 8 | *.tar.gz 9 | *.tar.bz2 10 | *.swo 11 | *.swp 12 | *~ 13 | /*.sublime-project 14 | /*.sublime-workspace 15 | /.DS_Store 16 | /.idea/ 17 | /.vscode/ 18 | 19 | # Build artifacts 20 | **/_output/ 21 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | KREPO = transformations 2 | KREPO_DESC = Triggermesh Transformation engine 3 | COMMANDS = transformation-controller transformation-adapter 4 | 5 | TARGETS ?= linux/amd64 6 | 7 | BASE_DIR ?= $(CURDIR) 8 | 9 | OUTPUT_DIR ?= $(BASE_DIR)/_output 10 | 11 | BIN_OUTPUT_DIR ?= $(OUTPUT_DIR) 12 | TEST_OUTPUT_DIR ?= $(OUTPUT_DIR) 13 | COVER_OUTPUT_DIR ?= $(OUTPUT_DIR) 14 | DIST_DIR ?= $(OUTPUT_DIR) 15 | 16 | DOCKER ?= docker 17 | IMAGE_REPO ?= gcr.io/triggermesh 18 | IMAGE_TAG ?= latest 19 | IMAGE_SHA ?= $(shell git rev-parse HEAD) 20 | 21 | GO ?= go 22 | GOFMT ?= gofmt 23 | GOLINT ?= golangci-lint run 24 | GOTOOL ?= go tool 25 | GOTEST ?= gotestsum --junitfile $(TEST_OUTPUT_DIR)/$(KREPO)-unit-tests.xml --format pkgname-and-test-fails -- 26 | 27 | KUBECTL ?= kubectl 28 | SED ?= sed 29 | 30 | GOPKGS = ./cmd/... ./pkg/apis/... ./pkg/pipeline/... ./pkg/reconciler/... 31 | LDFLAGS = -extldflags=-static -w -s 32 | 33 | HAS_GOTESTSUM := $(shell command -v gotestsum;) 34 | HAS_GOLANGCI_LINT := $(shell command -v golangci-lint;) 35 | 36 | .PHONY: help mod-download build install release test coverage lint fmt fmt-test images cloudbuild-test cloudbuild clean 37 | 38 | all: build 39 | 40 | install-gotestsum: 41 | ifndef HAS_GOTESTSUM 42 | curl -SL https://github.com/gotestyourself/gotestsum/releases/download/v0.4.2/gotestsum_0.4.2_linux_amd64.tar.gz | tar -C $(shell go env GOPATH)/bin -zxf - 43 | endif 44 | 45 | install-golangci-lint: 46 | ifndef HAS_GOLANGCI_LINT 47 | curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(shell go env GOPATH)/bin v1.26.0 48 | endif 49 | 50 | $(COMMANDS): 51 | @mkdir -p $(BIN_OUTPUT_DIR) 52 | $(GO) build -ldflags "$(LDFLAGS)" -o $(BIN_OUTPUT_DIR)/ ./cmd/$@ 53 | 54 | help: ## Display this help 55 | @awk 'BEGIN {FS = ":.*?## "; printf "\n$(KREPO_DESC)\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z0-9._-]+:.*?## / {printf " \033[36m%-20s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST) 56 | 57 | mod-download: ## Download go modules 58 | $(GO) mod download 59 | 60 | build: $(COMMANDS) ## Build the binary 61 | 62 | release: ## Build release artifacts 63 | @set -e ; \ 64 | for bin in $(COMMANDS) ; do \ 65 | for platform in $(TARGETS); do \ 66 | GOOS=$${platform%/*} ; \ 67 | GOARCH=$${platform#*/} ; \ 68 | RELEASE_BINARY=$$bin-$${GOOS}-$${GOARCH} ; \ 69 | [ $${GOOS} = "windows" ] && RELEASE_BINARY=$${RELEASE_BINARY}.exe ; \ 70 | echo "GOOS=$${GOOS} GOARCH=$${GOARCH} $(GO) build -ldflags "$(LDFLAGS)" -o $(DIST_DIR)/$${RELEASE_BINARY}" ./cmd/$$bin ; \ 71 | GOOS=$${GOOS} GOARCH=$${GOARCH} $(GO) build -ldflags "$(LDFLAGS)" -o $(DIST_DIR)/$${RELEASE_BINARY} ./cmd/$$bin ; \ 72 | done ; \ 73 | done 74 | $(KUBECTL) create -f config --dry-run=client -o yaml |\ 75 | $(SED) 's|ko://github.com/triggermesh/bumblebee/cmd/\(.*\)|$(IMAGE_REPO)/\1:${IMAGE_TAG}|' > $(DIST_DIR)/transformation.yaml 76 | 77 | test: install-gotestsum ## Run unit tests 78 | @mkdir -p $(TEST_OUTPUT_DIR) 79 | $(GOTEST) -p=1 -race -cover -coverprofile=$(TEST_OUTPUT_DIR)/$(KREPO)-c.out $(GOPKGS) 80 | 81 | cover: test ## Generate code coverage 82 | @mkdir -p $(COVER_OUTPUT_DIR) 83 | $(GOTOOL) cover -html=$(TEST_OUTPUT_DIR)/$(KREPO)-c.out -o $(COVER_OUTPUT_DIR)/$(KREPO)-coverage.html 84 | 85 | lint: install-golangci-lint ## Lint source files 86 | $(GOLINT) $(GOPKGS) 87 | 88 | fmt: ## Format source files 89 | $(GOFMT) -s -w $(shell $(GO) list -f '{{$$d := .Dir}}{{range .GoFiles}}{{$$d}}/{{.}} {{end}} {{$$d := .Dir}}{{range .TestGoFiles}}{{$$d}}/{{.}} {{end}}' $(GOPKGS)) 90 | 91 | fmt-test: ## Check source formatting 92 | @test -z $(shell $(GOFMT) -l $(shell $(GO) list -f '{{$$d := .Dir}}{{range .GoFiles}}{{$$d}}/{{.}} {{end}} {{$$d := .Dir}}{{range .TestGoFiles}}{{$$d}}/{{.}} {{end}}' $(GOPKGS))) 93 | 94 | IMAGES = $(foreach cmd,$(COMMANDS),$(cmd).image) 95 | images: $(IMAGES) ## Builds container images 96 | $(IMAGES): %.image: 97 | $(DOCKER) build -t $(IMAGE_REPO)/$* -f ./cmd/$*/Dockerfile . ; 98 | 99 | CLOUDBUILD_TEST = $(foreach cmd,$(COMMANDS),$(cmd).cloudbuild-test) 100 | cloudbuild-test: $(CLOUDBUILD_TEST) ## Test container image build with Google Cloud Build 101 | $(CLOUDBUILD_TEST): %.cloudbuild-test: 102 | # NOTE (antoineco): Cloud Build started failing recently with authentication errors when --no-push is specified. 103 | # Pushing images with the "_" tag is our hack to avoid those errors and ensure the build cache is always updated. 104 | gcloud builds submit $(BASE_DIR) --config cloudbuild.yaml --substitutions _CMD=$*,COMMIT_SHA=${IMAGE_SHA},_KANIKO_IMAGE_TAG=_ 105 | 106 | CLOUDBUILD = $(foreach cmd,$(COMMANDS),$(cmd).cloudbuild) 107 | cloudbuild: $(CLOUDBUILD) ## Build and publish image to GCR 108 | $(CLOUDBUILD): %.cloudbuild: 109 | gcloud builds submit $(BASE_DIR) --config cloudbuild.yaml --substitutions _CMD=$*,COMMIT_SHA=${IMAGE_SHA},_KANIKO_IMAGE_TAG=${IMAGE_TAG} 110 | 111 | clean: ## Clean build artifacts 112 | @for bin in $(COMMANDS) ; do \ 113 | for platform in $(TARGETS); do \ 114 | GOOS=$${platform%/*} ; \ 115 | GOARCH=$${platform#*/} ; \ 116 | RELEASE_BINARY=$$bin-$${GOOS}-$${GOARCH} ; \ 117 | [ $${GOOS} = "windows" ] && RELEASE_BINARY=$${RELEASE_BINARY}.exe ; \ 118 | $(RM) -v $(DIST_DIR)/$${RELEASE_BINARY}; \ 119 | done ; \ 120 | $(RM) -v $(BIN_OUTPUT_DIR)/$$bin; \ 121 | done 122 | @$(RM) -v $(DIST_DIR)/transformation.yaml 123 | @$(RM) -v $(TEST_OUTPUT_DIR)/$(KREPO)-c.out $(TEST_OUTPUT_DIR)/$(KREPO)-unit-tests.xml 124 | @$(RM) -v $(COVER_OUTPUT_DIR)/$(KREPO)-coverage.html 125 | 126 | # Code generation 127 | include $(BASE_DIR)/hack/inc.Codegen.mk 128 | -------------------------------------------------------------------------------- /cloudbuild.yaml: -------------------------------------------------------------------------------- 1 | steps: 2 | 3 | - name: gcr.io/kaniko-project/executor:v1.6.0-debug 4 | args: 5 | - --dockerfile=cmd/${_CMD}/Dockerfile 6 | - --destination=gcr.io/$PROJECT_ID/${_CMD}:${COMMIT_SHA} 7 | - --destination=gcr.io/$PROJECT_ID/${_CMD}:${_KANIKO_IMAGE_TAG} 8 | - --cache-repo=gcr.io/$PROJECT_ID/${_CMD}/cache 9 | - --cache=${_KANIKO_USE_BUILD_CACHE} 10 | - --no-push=${_KANIKO_NO_PUSH} 11 | - ${_KANIKO_EXTRA_ARGS} 12 | waitFor: ['-'] 13 | 14 | timeout: 1800s 15 | 16 | substitutions: 17 | _CMD: 18 | _KANIKO_IMAGE_TAG: latest 19 | _KANIKO_NO_PUSH: 'false' 20 | _KANIKO_USE_BUILD_CACHE: 'true' 21 | _KANIKO_EXTRA_ARGS: 22 | 23 | options: 24 | substitution_option: ALLOW_LOOSE 25 | 26 | tags: 27 | - transformation 28 | -------------------------------------------------------------------------------- /cmd/transformation-adapter/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.14-stretch AS builder 2 | 3 | ENV CGO_ENABLED 0 4 | ENV GOOS linux 5 | ENV GOARCH amd64 6 | 7 | WORKDIR /go/src/transformation-adapter 8 | 9 | COPY go.mod go.sum ./ 10 | RUN go mod download 11 | 12 | COPY . . 13 | RUN BIN_OUTPUT_DIR=/bin make transformation-adapter && \ 14 | mkdir /kodata && \ 15 | ls -lah hack && \ 16 | mv .git/* /kodata/ && \ 17 | rm -rf ${GOPATH} && \ 18 | rm -rf ${HOME}/.cache 19 | 20 | FROM scratch 21 | 22 | COPY --from=builder /kodata/ ${KO_DATA_PATH}/ 23 | COPY --from=builder /bin/transformation-adapter / 24 | COPY licenses/ /licenses/ 25 | 26 | ENTRYPOINT ["/transformation-adapter"] 27 | -------------------------------------------------------------------------------- /cmd/transformation-adapter/kodata/HEAD: -------------------------------------------------------------------------------- 1 | ../../../.git/HEAD -------------------------------------------------------------------------------- /cmd/transformation-adapter/kodata/refs: -------------------------------------------------------------------------------- 1 | ../../../.git/refs -------------------------------------------------------------------------------- /cmd/transformation-adapter/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 TriggerMesh Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package main 18 | 19 | import ( 20 | "context" 21 | "encoding/json" 22 | "log" 23 | 24 | "github.com/kelseyhightower/envconfig" 25 | 26 | "github.com/triggermesh/bumblebee/pkg/apis/transformation/v1alpha1" 27 | "github.com/triggermesh/bumblebee/pkg/pipeline" 28 | ) 29 | 30 | type envConfig struct { 31 | // Sink URL where to send cloudevents 32 | Sink string `envconfig:"K_SINK"` 33 | 34 | // Transformation specifications 35 | TransformationContext string `envconfig:"TRANSFORMATION_CONTEXT"` 36 | TransformationData string `envconfig:"TRANSFORMATION_DATA"` 37 | } 38 | 39 | func main() { 40 | var env envConfig 41 | if err := envconfig.Process("", &env); err != nil { 42 | log.Fatalf("Failed to process env var: %v", err) 43 | } 44 | 45 | ctx, cancel := context.WithCancel(context.Background()) 46 | defer cancel() 47 | 48 | trnContext, trnData := []v1alpha1.Transform{}, []v1alpha1.Transform{} 49 | err := json.Unmarshal([]byte(env.TransformationContext), &trnContext) 50 | if err != nil { 51 | log.Fatalf("Cannot unmarshal Context Transformation variable: %v", err) 52 | } 53 | err = json.Unmarshal([]byte(env.TransformationData), &trnData) 54 | if err != nil { 55 | log.Fatalf("Cannot unmarshal Data Transformation variable: %v", err) 56 | } 57 | 58 | handler, err := pipeline.NewHandler(trnContext, trnData) 59 | if err != nil { 60 | log.Fatalf("Cannot create transformation handler: %v", err) 61 | } 62 | 63 | if err := handler.Start(ctx, env.Sink); err != nil { 64 | log.Fatalf("Transformation handler: %v", err) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /cmd/transformation-controller/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.14-stretch AS builder 2 | 3 | ENV CGO_ENABLED 0 4 | ENV GOOS linux 5 | ENV GOARCH amd64 6 | 7 | WORKDIR /go/src/transformation-controller 8 | 9 | COPY go.mod go.sum ./ 10 | RUN go mod download 11 | 12 | COPY . . 13 | RUN BIN_OUTPUT_DIR=/bin make transformation-controller && \ 14 | mkdir /kodata && \ 15 | ls -lah hack && \ 16 | mv .git/* /kodata/ && \ 17 | rm -rf ${GOPATH} && \ 18 | rm -rf ${HOME}/.cache 19 | 20 | FROM scratch 21 | 22 | # Emulate ko builds 23 | # https://github.com/google/ko/blob/v0.5.0/README.md#including-static-assets 24 | ENV KO_DATA_PATH /kodata 25 | 26 | COPY --from=builder /kodata/ ${KO_DATA_PATH}/ 27 | COPY --from=builder /bin/transformation-controller / 28 | COPY licenses/ /licenses/ 29 | 30 | ENTRYPOINT ["/transformation-controller"] 31 | -------------------------------------------------------------------------------- /cmd/transformation-controller/kodata/HEAD: -------------------------------------------------------------------------------- 1 | ../../../.git/HEAD -------------------------------------------------------------------------------- /cmd/transformation-controller/kodata/refs: -------------------------------------------------------------------------------- 1 | ../../../.git/refs -------------------------------------------------------------------------------- /cmd/transformation-controller/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020-2021 TriggerMesh Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package main 18 | 19 | import ( 20 | // The set of controllers this controller process runs. 21 | "github.com/triggermesh/bumblebee/pkg/reconciler/controller" 22 | 23 | // This defines the shared main for injected controllers. 24 | "knative.dev/pkg/injection/sharedmain" 25 | ) 26 | 27 | func main() { 28 | sharedmain.Main("bumblebee-controller", controller.NewController) 29 | } 30 | -------------------------------------------------------------------------------- /config/100-namespace.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Triggermesh Inc. 2 | # 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 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: v1 16 | kind: Namespace 17 | metadata: 18 | name: triggermesh 19 | -------------------------------------------------------------------------------- /config/200-controller-clusterrole.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Triggermesh Inc. 2 | # 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 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | kind: ClusterRole 16 | apiVersion: rbac.authorization.k8s.io/v1 17 | metadata: 18 | name: transformation-controller 19 | labels: 20 | rbac.triggermesh.io/transformation-controller: "true" 21 | aggregationRule: 22 | clusterRoleSelectors: 23 | - matchLabels: 24 | transformation.flow.triggermesh.io/controller: "true" 25 | rules: [] # Rules are automatically filled in by the controller manager. 26 | 27 | --- 28 | 29 | kind: ClusterRole 30 | apiVersion: rbac.authorization.k8s.io/v1 31 | metadata: 32 | name: transformation-core 33 | labels: 34 | transformation.flow.triggermesh.io/controller: "true" 35 | rules: 36 | - apiGroups: 37 | - flow.triggermesh.io 38 | resources: 39 | - transformations 40 | verbs: 41 | - get 42 | - list 43 | - watch 44 | - apiGroups: 45 | - flow.triggermesh.io 46 | resources: 47 | - transformations/status 48 | verbs: 49 | - update 50 | 51 | # Knative's sharedmain method supports leader election via coordination lease mechanism. 52 | # This role is needed to let controller work with lease resources. 53 | # https://docs.google.com/document/d/1zHtVjVmzivO2PEhZkpqNo-M9cPnmfvBWMYzP8ehVXtM/edit# 54 | # https://github.com/knative/pkg/pull/1019 55 | - apiGroups: 56 | - coordination.k8s.io 57 | resources: 58 | - leases 59 | verbs: 60 | - get 61 | - list 62 | - create 63 | - update 64 | - delete 65 | - patch 66 | - watch 67 | 68 | # Transformation controller creates the Knative service as its workhorse 69 | # to handle the CloudEvents and apply transformations, so it needs to have 70 | # a control over the Service resources. 71 | - apiGroups: 72 | - serving.knative.dev 73 | resources: 74 | - services 75 | verbs: 76 | - get 77 | - list 78 | - create 79 | - update 80 | - delete 81 | - patch 82 | - watch 83 | 84 | # Record Kubernetes events 85 | - apiGroups: 86 | - '' 87 | resources: 88 | - events 89 | verbs: 90 | - create 91 | - patch 92 | - update 93 | 94 | # Read controller configurations 95 | - apiGroups: 96 | - '' 97 | resources: 98 | - configmaps 99 | verbs: 100 | - list 101 | - watch 102 | 103 | - apiGroups: 104 | - '' 105 | resources: 106 | - configmaps 107 | resourceNames: 108 | - config-logging 109 | - config-observability 110 | - config-leader-election 111 | verbs: 112 | - get 113 | 114 | --- 115 | 116 | # The role is needed for the aggregated role addressable-resolver in knative-eventing to provide readonly access to "Addressables". 117 | # see https://github.com/knative/eventing/blob/release-0.16/docs/spec/channel.md#aggregated-addressable-resolver-clusterrole 118 | apiVersion: rbac.authorization.k8s.io/v1 119 | kind: ClusterRole 120 | metadata: 121 | name: transformation-addressable-resolver 122 | labels: 123 | duck.knative.dev/addressable: "true" 124 | rules: 125 | - apiGroups: 126 | - flow.triggermesh.io 127 | resources: 128 | - transformations 129 | - transformations/status 130 | verbs: 131 | - get 132 | - list 133 | - watch 134 | -------------------------------------------------------------------------------- /config/200-serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Triggermesh Inc. 2 | # 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 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: v1 16 | kind: ServiceAccount 17 | metadata: 18 | name: transformation-controller 19 | namespace: triggermesh 20 | -------------------------------------------------------------------------------- /config/200-user-clusterrole.yaml: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2020 TriggerMesh Inc. 2 | # 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 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # Labels rbac.triggermesh.io/transformation-admin: "true" is aggregated for 16 | # all Triggermesh users and Bridge controller 17 | 18 | apiVersion: rbac.authorization.k8s.io/v1 19 | kind: ClusterRole 20 | metadata: 21 | name: transformation-admin 22 | labels: 23 | rbac.triggermesh.io/transformation-admin: "true" 24 | rules: 25 | - apiGroups: 26 | - flow.triggermesh.io 27 | resources: 28 | - transformations 29 | verbs: 30 | - get 31 | - list 32 | - create 33 | - update 34 | - patch 35 | - delete 36 | - watch 37 | -------------------------------------------------------------------------------- /config/201-clusterrolebinding.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Triggermesh Inc. 2 | # 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 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: rbac.authorization.k8s.io/v1 16 | kind: ClusterRoleBinding 17 | metadata: 18 | name: transformation-controller-admin 19 | subjects: 20 | - kind: ServiceAccount 21 | name: transformation-controller 22 | namespace: triggermesh 23 | roleRef: 24 | kind: ClusterRole 25 | name: transformation-controller 26 | apiGroup: rbac.authorization.k8s.io 27 | -------------------------------------------------------------------------------- /config/300-transformation.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2020 TriggerMesh Inc. 2 | # 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 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: apiextensions.k8s.io/v1 16 | kind: CustomResourceDefinition 17 | metadata: 18 | name: transformations.flow.triggermesh.io 19 | spec: 20 | group: flow.triggermesh.io 21 | scope: Namespaced 22 | names: 23 | kind: Transformation 24 | plural: transformations 25 | categories: 26 | - all 27 | - knative 28 | - eventing 29 | - transformations 30 | shortNames: 31 | - trn 32 | versions: 33 | - name: v1alpha1 34 | served: true 35 | storage: true 36 | subresources: 37 | status: {} 38 | schema: 39 | openAPIV3Schema: 40 | description: TriggerMesh CloudEvents transformation engine. 41 | type: object 42 | properties: 43 | spec: 44 | description: Desired state of the transformation object. 45 | type: object 46 | properties: 47 | context: 48 | description: CloudEvents Context attributes transformation spec. 49 | type: array 50 | items: 51 | description: The list of transformation operations executed on the event context sequentially. 52 | type: object 53 | properties: 54 | operation: 55 | description: Name of the transformation operation. 56 | type: string 57 | enum: ['add', 'delete', 'shift', 'store'] 58 | paths: 59 | description: Key-value event pairs to apply the transformations on. 60 | type: array 61 | items: 62 | type: object 63 | properties: 64 | key: 65 | description: JSON path or variable name. Depends on the operation type. 66 | nullable: true 67 | type: string 68 | value: 69 | description: JSON path or variable name. Depends on the operation type. 70 | nullable: true 71 | type: string 72 | required: 73 | - operation 74 | data: 75 | description: CloudEvents Data transformation spec. 76 | type: array 77 | items: 78 | description: The list of transformation operations executed on the event data sequentially. 79 | type: object 80 | properties: 81 | operation: 82 | description: Name of the transformation operation. 83 | type: string 84 | enum: ['add', 'delete', 'shift', 'store'] 85 | paths: 86 | description: Key-value event pairs to apply the transformations on. 87 | type: array 88 | items: 89 | type: object 90 | properties: 91 | key: 92 | description: JSON path or variable name. Depends on the operation type. 93 | nullable: true 94 | type: string 95 | value: 96 | description: JSON path or variable name. Depends on the operation type. 97 | nullable: true 98 | type: string 99 | required: 100 | - operation 101 | sink: 102 | description: The destination of events sourced from the transformation object. 103 | type: object 104 | properties: 105 | ref: 106 | description: Reference to an addressable Kubernetes object to be used as the destination of events. 107 | type: object 108 | properties: 109 | apiVersion: 110 | type: string 111 | kind: 112 | type: string 113 | namespace: 114 | type: string 115 | name: 116 | type: string 117 | required: 118 | - apiVersion 119 | - kind 120 | - name 121 | uri: 122 | description: URI to use as the destination of events. 123 | type: string 124 | format: uri 125 | oneOf: 126 | - required: ['ref'] 127 | - required: ['uri'] 128 | status: 129 | description: Reported status of Transformation. 130 | type: object 131 | properties: 132 | sinkUri: 133 | description: URI of the sink where events are currently sent to. 134 | type: string 135 | format: uri 136 | ceAttributes: 137 | description: CloudEvents context attributes overrides. 138 | type: array 139 | items: 140 | type: object 141 | properties: 142 | type: 143 | type: string 144 | source: 145 | type: string 146 | observedGeneration: 147 | type: integer 148 | format: int64 149 | conditions: 150 | type: array 151 | items: 152 | type: object 153 | properties: 154 | type: 155 | type: string 156 | status: 157 | type: string 158 | enum: ['True', 'False', Unknown] 159 | severity: 160 | type: string 161 | enum: [Error, Warning, Info] 162 | reason: 163 | type: string 164 | message: 165 | type: string 166 | lastTransitionTime: 167 | type: string 168 | format: date-time 169 | required: 170 | - type 171 | - status 172 | address: 173 | description: Address of the HTTP/S endpoint where Transformation is serving incoming CloudEvents. 174 | type: object 175 | properties: 176 | url: 177 | type: string 178 | additionalPrinterColumns: 179 | - name: Address 180 | type: string 181 | jsonPath: .status.address.url 182 | - name: Ready 183 | type: string 184 | jsonPath: ".status.conditions[?(@.type=='Ready')].status" 185 | - name: Reason 186 | type: string 187 | jsonPath: ".status.conditions[?(@.type=='Ready')].reason" 188 | -------------------------------------------------------------------------------- /config/500-controller.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Triggermesh Inc. 2 | # 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 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: apps/v1 16 | kind: Deployment 17 | metadata: 18 | name: transformation-controller 19 | namespace: triggermesh 20 | spec: 21 | replicas: 1 22 | selector: 23 | matchLabels: 24 | app: transformation-controller 25 | template: 26 | metadata: 27 | labels: 28 | app: transformation-controller 29 | spec: 30 | serviceAccountName: transformation-controller 31 | containers: 32 | - name: controller 33 | image: ko://github.com/triggermesh/bumblebee/cmd/transformation-controller 34 | ports: 35 | - name: metrics 36 | containerPort: 9090 37 | env: 38 | - name: TRANSFORMER_IMAGE 39 | value: ko://github.com/triggermesh/bumblebee/cmd/transformation-adapter 40 | - name: SYSTEM_NAMESPACE 41 | valueFrom: 42 | fieldRef: 43 | fieldPath: metadata.namespace 44 | - name: CONFIG_LOGGING_NAME 45 | value: config-logging 46 | - name: CONFIG_OBSERVABILITY_NAME 47 | value: config-observability 48 | - name: METRICS_DOMAIN 49 | value: knative.dev/transformation 50 | -------------------------------------------------------------------------------- /config/config-leader-election.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Triggermesh Inc. 2 | # 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 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: v1 16 | kind: ConfigMap 17 | metadata: 18 | name: config-leader-election 19 | namespace: triggermesh 20 | data: 21 | _example: | 22 | ################################ 23 | # # 24 | # EXAMPLE CONFIGURATION # 25 | # # 26 | ################################ 27 | # This block is not actually functional configuration, 28 | # but serves to illustrate the available configuration 29 | # options and document them in a way that is accessible 30 | # to users that `kubectl edit` this config map. 31 | # 32 | # These sample configuration options may be copied out of 33 | # this example block and unindented to be in the data block 34 | # to actually change the configuration. 35 | # leaseDuration is how long non-leaders will wait to try to acquire the 36 | # lock; 15 seconds is the value used by core kubernetes controllers. 37 | leaseDuration: "15s" 38 | # renewDeadline is how long a leader will try to renew the lease before 39 | # giving up; 10 seconds is the value used by core kubernetes controllers. 40 | renewDeadline: "10s" 41 | # retryPeriod is how long the leader election client waits between tries of 42 | # actions; 2 seconds is the value used by core kubernetes controllers. 43 | retryPeriod: "2s" 44 | # enabledComponents is a comma-delimited list of component names for which 45 | # leader election is enabled. Valid values are: 46 | enabledComponents: "controller,webhook" 47 | -------------------------------------------------------------------------------- /config/config-logging.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Triggermesh Inc. 2 | # 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 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: v1 16 | kind: ConfigMap 17 | metadata: 18 | name: config-logging 19 | namespace: triggermesh 20 | 21 | data: 22 | _example: | 23 | ################################ 24 | # # 25 | # EXAMPLE CONFIGURATION # 26 | # # 27 | ################################ 28 | 29 | # This block is not actually functional configuration, 30 | # but serves to illustrate the available configuration 31 | # options and document them in a way that is accessible 32 | # to users that `kubectl edit` this config map. 33 | # 34 | # These sample configuration options may be copied out of 35 | # this example block and unindented to be in the data block 36 | # to actually change the configuration. 37 | 38 | # Common configuration for all Knative codebase 39 | zap-logger-config: | 40 | { 41 | "level": "info", 42 | "development": false, 43 | "outputPaths": ["stdout"], 44 | "errorOutputPaths": ["stderr"], 45 | "encoding": "json", 46 | "encoderConfig": { 47 | "timeKey": "ts", 48 | "levelKey": "level", 49 | "nameKey": "logger", 50 | "callerKey": "caller", 51 | "messageKey": "msg", 52 | "stacktraceKey": "stacktrace", 53 | "lineEnding": "", 54 | "levelEncoder": "", 55 | "timeEncoder": "iso8601", 56 | "durationEncoder": "", 57 | "callerEncoder": "" 58 | } 59 | } 60 | 61 | # Log level overrides 62 | # Changes are be picked up immediately. 63 | loglevel.controller: "info" 64 | loglevel.webhook: "info" 65 | -------------------------------------------------------------------------------- /config/config-observability.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Triggermesh Inc. 2 | # 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 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: v1 16 | kind: ConfigMap 17 | metadata: 18 | name: config-observability 19 | namespace: triggermesh 20 | 21 | data: 22 | _example: | 23 | ################################ 24 | # # 25 | # EXAMPLE CONFIGURATION # 26 | # # 27 | ################################ 28 | 29 | # This block is not actually functional configuration, 30 | # but serves to illustrate the available configuration 31 | # options and document them in a way that is accessible 32 | # to users that `kubectl edit` this config map. 33 | # 34 | # These sample configuration options may be copied out of 35 | # this example block and unindented to be in the data block 36 | # to actually change the configuration. 37 | 38 | # If non-empty, this enables queue proxy writing request logs to stdout. 39 | # The value determines the shape of the request logs and it must be a valid go text/template. 40 | # It is important to keep this as a single line. Multiple lines are parsed as separate entities 41 | # by most collection agents and will split the request logs into multiple records. 42 | # 43 | # The following fields and functions are available to the template: 44 | # 45 | # Request: An http.Request (see https://golang.org/pkg/net/http/#Request) 46 | # representing an HTTP request received by the server. 47 | # 48 | # Response: 49 | # struct { 50 | # Code int // HTTP status code (see https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml) 51 | # Size int // An int representing the size of the response. 52 | # Latency float64 // A float64 representing the latency of the response in seconds. 53 | # } 54 | # 55 | # Revision: 56 | # struct { 57 | # Name string // Knative revision name 58 | # Namespace string // Knative revision namespace 59 | # Service string // Knative service name 60 | # Configuration string // Knative configuration name 61 | # PodName string // Name of the pod hosting the revision 62 | # PodIP string // IP of the pod hosting the revision 63 | # } 64 | # 65 | logging.request-log-template: '{"httpRequest": {"requestMethod": "{{.Request.Method}}", "requestUrl": "{{js .Request.RequestURI}}", "requestSize": "{{.Request.ContentLength}}", "status": {{.Response.Code}}, "responseSize": "{{.Response.Size}}", "userAgent": "{{js .Request.UserAgent}}", "remoteIp": "{{js .Request.RemoteAddr}}", "serverIp": "{{.Revision.PodIP}}", "referer": "{{js .Request.Referer}}", "latency": "{{.Response.Latency}}s", "protocol": "{{.Request.Proto}}"}, "traceId": "{{index .Request.Header "X-B3-Traceid"}}"}' 66 | 67 | # metrics.backend-destination field specifies the system metrics destination. 68 | # It supports either prometheus (the default) or stackdriver. 69 | # Note: Using stackdriver will incur additional charges 70 | metrics.backend-destination: prometheus 71 | 72 | # metrics.request-metrics-backend-destination specifies the request metrics 73 | # destination. If non-empty, it enables queue proxy to send request metrics. 74 | # Currently supported values: prometheus, stackdriver. 75 | metrics.request-metrics-backend-destination: prometheus 76 | 77 | # metrics.stackdriver-project-id field specifies the stackdriver project ID. This 78 | # field is optional. When running on GCE, application default credentials will be 79 | # used if this field is not provided. 80 | metrics.stackdriver-project-id: "" 81 | 82 | # metrics.allow-stackdriver-custom-metrics indicates whether it is allowed to send metrics to 83 | # Stackdriver using "global" resource type and custom metric type if the 84 | # metrics are not supported by "knative_revision" resource type. Setting this 85 | # flag to "true" could cause extra Stackdriver charge. 86 | # If metrics.backend-destination is not Stackdriver, this is ignored. 87 | metrics.allow-stackdriver-custom-metrics: "false" 88 | -------------------------------------------------------------------------------- /config/samples/broker.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: eventing.knative.dev/v1 2 | kind: Broker 3 | metadata: 4 | annotations: 5 | eventing.knative.dev/broker.class: MTChannelBasedBroker 6 | name: transformation-demo 7 | spec: 8 | config: 9 | apiVersion: v1 10 | kind: ConfigMap 11 | name: config-br-default-channel 12 | namespace: knative-eventing 13 | -------------------------------------------------------------------------------- /config/samples/multi-target/github-transformation.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: eventing.knative.dev/v1 2 | kind: Trigger 3 | metadata: 4 | name: github-push-transformation-trigger 5 | spec: 6 | broker: transformation-demo 7 | filter: 8 | attributes: 9 | type: dev.knative.source.github.push 10 | subscriber: 11 | ref: 12 | apiVersion: flow.triggermesh.io/v1alpha1 13 | kind: Transformation 14 | name: github-push-transformation 15 | 16 | --- 17 | 18 | apiVersion: flow.triggermesh.io/v1alpha1 19 | kind: Transformation 20 | metadata: 21 | name: github-push-transformation 22 | spec: 23 | context: 24 | - operation: store 25 | paths: 26 | - key: $ceType 27 | value: type 28 | 29 | - operation: add 30 | paths: 31 | - key: type 32 | value: io.triggermesh.transformation.github 33 | 34 | data: 35 | - operation: store 36 | paths: 37 | - key: $repository 38 | value: repository.name 39 | - key: $message 40 | value: head_commit.message 41 | - key: $author 42 | value: head_commit.author.username 43 | 44 | - operation: delete 45 | paths: 46 | - key: 47 | 48 | - operation: add 49 | paths: 50 | - key: type 51 | value: $ceType 52 | - key: object 53 | value: $author 54 | - key: subject 55 | value: $repository 56 | - key: verb 57 | value: $message 58 | 59 | --- 60 | 61 | apiVersion: eventing.knative.dev/v1 62 | kind: Trigger 63 | metadata: 64 | name: github-issue-transformation-trigger 65 | spec: 66 | broker: transformation-demo 67 | filter: 68 | attributes: 69 | type: dev.knative.source.github.issues 70 | subscriber: 71 | ref: 72 | apiVersion: flow.triggermesh.io/v1alpha1 73 | kind: Transformation 74 | name: github-issue-transformation 75 | 76 | --- 77 | 78 | apiVersion: flow.triggermesh.io/v1alpha1 79 | kind: Transformation 80 | metadata: 81 | name: github-issue-transformation 82 | spec: 83 | context: 84 | - operation: store 85 | paths: 86 | - key: $ceType 87 | value: type 88 | 89 | - operation: add 90 | paths: 91 | - key: type 92 | value: io.triggermesh.transformation.github 93 | 94 | data: 95 | - operation: store 96 | paths: 97 | - key: $action 98 | value: action 99 | - key: $repository 100 | value: repository.name 101 | - key: $title 102 | value: issue.title 103 | - key: $author 104 | value: issue.user.login 105 | 106 | - operation: delete 107 | paths: 108 | - key: 109 | 110 | - operation: add 111 | paths: 112 | - key: type 113 | value: $ceType 114 | - key: object 115 | value: $author 116 | - key: subject 117 | value: $repository 118 | - key: verb 119 | value: $action issue "$title" 120 | -------------------------------------------------------------------------------- /config/samples/multi-target/githubsource.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: githubsecret 5 | data: 6 | accessToken: 7 | secretToken: 8 | 9 | --- 10 | 11 | apiVersion: sources.knative.dev/v1alpha1 12 | kind: GitHubSource 13 | metadata: 14 | name: githubsource-transformation-demo 15 | spec: 16 | accessToken: 17 | secretKeyRef: 18 | key: accessToken 19 | name: githubsecret 20 | secretToken: 21 | secretKeyRef: 22 | key: secretToken 23 | name: githubsecret 24 | eventTypes: 25 | - push 26 | - issues 27 | ownerAndRepository: tzununbekov/foo 28 | sink: 29 | ref: 30 | apiVersion: eventing.knative.dev/v1 31 | kind: Broker 32 | name: transformation-demo 33 | -------------------------------------------------------------------------------- /config/samples/multi-target/googlesheet-target.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: googlesheet 5 | data: 6 | credentials: 7 | 8 | --- 9 | 10 | apiVersion: eventing.knative.dev/v1 11 | kind: Trigger 12 | metadata: 13 | name: googlesheet-transformation-trigger 14 | spec: 15 | broker: transformation-demo 16 | filter: 17 | attributes: 18 | type: io.triggermesh.transformation.github 19 | subscriber: 20 | ref: 21 | apiVersion: flow.triggermesh.io/v1alpha1 22 | kind: Transformation 23 | name: googlesheet-transformation 24 | 25 | --- 26 | 27 | apiVersion: flow.triggermesh.io/v1alpha1 28 | kind: Transformation 29 | metadata: 30 | name: googlesheet-transformation 31 | spec: 32 | context: 33 | - operation: add 34 | paths: 35 | - key: type 36 | value: io.triggermesh.transformation.googlesheet 37 | 38 | data: 39 | - operation: store 40 | paths: 41 | - key: $type 42 | value: type 43 | - key: $object 44 | value: object 45 | - key: $subject 46 | value: subject 47 | - key: $verb 48 | value: verb 49 | 50 | - operation: delete 51 | paths: 52 | - key: 53 | 54 | - operation: add 55 | paths: 56 | - key: message 57 | value: 'type: $type, user: $object, repository: $subject, action: $verb' 58 | 59 | --- 60 | 61 | apiVersion: eventing.knative.dev/v1 62 | kind: Trigger 63 | metadata: 64 | name: googlesheet-target-trigger 65 | spec: 66 | broker: transformation-demo 67 | filter: 68 | attributes: 69 | type: io.triggermesh.transformation.googlesheet 70 | subscriber: 71 | ref: 72 | apiVersion: targets.triggermesh.io/v1alpha1 73 | kind: GoogleSheetTarget 74 | name: transformation-googlesheet-demo 75 | 76 | --- 77 | 78 | apiVersion: targets.triggermesh.io/v1alpha1 79 | kind: GoogleSheetTarget 80 | metadata: 81 | name: transformation-googlesheet-demo 82 | spec: 83 | id: 1nmznXLnrziNbAnKTayH4y-uxOsTC3gxUNqRgIfXP_X4 84 | defaultPrefix: transformation-demo 85 | googleServiceAccount: 86 | secretKeyRef: 87 | name: googlesheet 88 | key: credentials 89 | -------------------------------------------------------------------------------- /config/samples/multi-target/slack-target.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: slacktarget 5 | type: Opaque 6 | data: 7 | token: 8 | 9 | --- 10 | 11 | apiVersion: eventing.knative.dev/v1 12 | kind: Trigger 13 | metadata: 14 | name: slack-transformation-trigger 15 | spec: 16 | broker: transformation-demo 17 | filter: 18 | attributes: 19 | type: io.triggermesh.transformation.github 20 | subscriber: 21 | ref: 22 | apiVersion: flow.triggermesh.io/v1alpha1 23 | kind: Transformation 24 | name: slack-transformation 25 | 26 | --- 27 | 28 | apiVersion: flow.triggermesh.io/v1alpha1 29 | kind: Transformation 30 | metadata: 31 | name: slack-transformation 32 | spec: 33 | context: 34 | - operation: add 35 | paths: 36 | - key: type 37 | value: com.slack.webapi.chat.postMessage 38 | 39 | data: 40 | - operation: store 41 | paths: 42 | - key: $object 43 | value: object 44 | - key: $subject 45 | value: subject 46 | - key: $verb 47 | value: verb 48 | 49 | - operation: delete 50 | paths: 51 | - key: 52 | 53 | - operation: add 54 | paths: 55 | - key: channel 56 | value: github-demo-channel 57 | - key: text 58 | value: '$object at $subject: $verb' 59 | 60 | --- 61 | 62 | apiVersion: eventing.knative.dev/v1 63 | kind: Trigger 64 | metadata: 65 | name: slack-target-trigger 66 | spec: 67 | broker: transformation-demo 68 | filter: 69 | attributes: 70 | type: com.slack.webapi.chat.postMessage 71 | subscriber: 72 | ref: 73 | apiVersion: targets.triggermesh.io/v1alpha1 74 | kind: SlackTarget 75 | name: transformation-slack-demo 76 | 77 | --- 78 | 79 | apiVersion: targets.triggermesh.io/v1alpha1 80 | kind: SlackTarget 81 | metadata: 82 | name: transformation-slack-demo 83 | spec: 84 | token: 85 | secretKeyRef: 86 | name: slacktarget 87 | key: token 88 | -------------------------------------------------------------------------------- /config/samples/simple-event/event-display.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: serving.knative.dev/v1 2 | kind: Service 3 | metadata: 4 | name: event-display 5 | spec: 6 | template: 7 | metadata: 8 | annotations: 9 | autoscaling.knative.dev/minScale: "1" 10 | spec: 11 | containers: 12 | - image: gcr.io/knative-releases/knative.dev/eventing-contrib/cmd/event_display 13 | -------------------------------------------------------------------------------- /config/samples/simple-event/pingsource.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: sources.knative.dev/v1 2 | kind: PingSource 3 | metadata: 4 | name: ping-source 5 | spec: 6 | schedule: "*/1 * * * *" 7 | contentType: "application/json" 8 | data: '{ 9 | "First Name": "Barbara", 10 | "Last Name": "Singh", 11 | "Date of birth": { 12 | "year": 1955, 13 | "month": 1, 14 | "day" : 23 15 | }, 16 | "Married": true, 17 | "Children": 18 | [ 19 | {"Name": "Martin", "Year of birth": 1980}, 20 | {"Name": "Margaret", "Year of birth": 1983} 21 | ], 22 | "Mobile phone": null 23 | }' 24 | sink: 25 | ref: 26 | apiVersion: eventing.knative.dev/v1 27 | kind: Broker 28 | name: transformation-demo 29 | -------------------------------------------------------------------------------- /config/samples/simple-event/transformation.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: flow.triggermesh.io/v1alpha1 2 | kind: Transformation 3 | metadata: 4 | name: pingsource-transformation 5 | spec: 6 | # sink: 7 | # ref: 8 | # apiVersion: serving.knative.dev/v1 9 | # kind: Service 10 | # name: event-display 11 | context: 12 | - operation: store 13 | paths: 14 | - key: $time 15 | value: time 16 | - key: $id 17 | value: id 18 | 19 | - operation: add 20 | paths: 21 | - key: id 22 | value: $person-$id 23 | - key: type 24 | value: io.triggermesh.transformation.pingsource 25 | 26 | data: 27 | - operation: store 28 | paths: 29 | - key: $person 30 | value: First Name 31 | 32 | - operation: add 33 | paths: 34 | - key: event.ID 35 | value: $id 36 | - key: event.time 37 | value: $time 38 | 39 | - operation: shift 40 | paths: 41 | - key: Date of birth:birthday 42 | - key: First Name:firstname 43 | - key: Last Name:lastname 44 | 45 | - operation: delete 46 | paths: 47 | - key: Mobile phone 48 | - key: Children[1].Year of birth 49 | - value: Martin 50 | -------------------------------------------------------------------------------- /config/samples/simple-event/triggers.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: eventing.knative.dev/v1 2 | kind: Trigger 3 | metadata: 4 | name: pingsource-transformation-trigger 5 | spec: 6 | broker: transformation-demo 7 | filter: 8 | attributes: 9 | type: dev.knative.sources.ping 10 | subscriber: 11 | ref: 12 | apiVersion: flow.triggermesh.io/v1alpha1 13 | kind: Transformation 14 | name: pingsource-transformation 15 | 16 | --- 17 | 18 | apiVersion: eventing.knative.dev/v1 19 | kind: Trigger 20 | metadata: 21 | name: eventdisplay-transformation-trigger 22 | spec: 23 | broker: transformation-demo 24 | filter: 25 | attributes: 26 | type: io.triggermesh.transformation.pingsource 27 | subscriber: 28 | ref: 29 | apiVersion: serving.knative.dev/v1 30 | kind: Service 31 | name: event-display 32 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/triggermesh/bumblebee 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/cloudevents/sdk-go/v2 v2.2.0 7 | github.com/kelseyhightower/envconfig v1.4.0 8 | github.com/stretchr/testify v1.7.0 9 | go.uber.org/zap v1.17.0 10 | k8s.io/api v0.19.7 11 | k8s.io/apimachinery v0.19.7 12 | k8s.io/client-go v0.19.7 13 | k8s.io/code-generator v0.19.7 // indirect 14 | knative.dev/networking v0.0.0-20210603073844-5521a8b92648 15 | knative.dev/pkg v0.0.0-20210602095030-0e61d6763dd6 16 | knative.dev/serving v0.22.2 17 | sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06 // indirect 18 | sigs.k8s.io/structured-merge-diff/v3 v3.0.0 // indirect 19 | ) 20 | -------------------------------------------------------------------------------- /hack/boilerplate/boilerplate.go.txt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 TriggerMesh Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | -------------------------------------------------------------------------------- /hack/inc.Codegen.mk: -------------------------------------------------------------------------------- 1 | # Code generation 2 | # 3 | # see https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api_changes.md#generate-code 4 | 5 | # Name of the Go package for this repository 6 | PKG := github.com/triggermesh/bumblebee 7 | 8 | # List of API groups to generate code for 9 | # e.g. "transformation/v1alpha1 transformation/v1alpha2" 10 | API_GROUPS := transformation/v1alpha1 11 | # generates e.g. "PKG/pkg/apis/transformation/v1alpha1 PKG/pkg/apis/transformation/v1alpha2" 12 | api-import-paths := $(foreach group,$(API_GROUPS),$(PKG)/pkg/apis/$(group)) 13 | 14 | generators := deepcopy client lister informer injection 15 | 16 | .PHONY: codegen $(generators) 17 | 18 | codegen: $(generators) 19 | 20 | # http://blog.jgc.org/2007/06/escaping-comma-and-space-in-gnu-make.html 21 | comma := , 22 | space := 23 | space += 24 | 25 | deepcopy: 26 | @echo "+ Generating deepcopy funcs for $(API_GROUPS)" 27 | @go run k8s.io/code-generator/cmd/deepcopy-gen \ 28 | --go-header-file hack/boilerplate/boilerplate.go.txt \ 29 | --input-dirs $(subst $(space),$(comma),$(api-import-paths)) 30 | 31 | client: 32 | @echo "+ Generating clientsets for $(API_GROUPS)" 33 | @rm -rf pkg/client/generated/clientset 34 | @go run k8s.io/code-generator/cmd/client-gen \ 35 | --go-header-file hack/boilerplate/boilerplate.go.txt \ 36 | --input $(subst $(space),$(comma),$(API_GROUPS)) \ 37 | --input-base $(PKG)/pkg/apis \ 38 | --output-package $(PKG)/pkg/client/generated/clientset 39 | 40 | lister: 41 | @echo "+ Generating listers for $(API_GROUPS)" 42 | @rm -rf pkg/client/generated/listers 43 | @go run k8s.io/code-generator/cmd/lister-gen \ 44 | --go-header-file hack/boilerplate/boilerplate.go.txt \ 45 | --input-dirs $(subst $(space),$(comma),$(api-import-paths)) \ 46 | --output-package $(PKG)/pkg/client/generated/listers 47 | 48 | informer: 49 | @echo "+ Generating informers for $(API_GROUPS)" 50 | @rm -rf pkg/client/generated/informers 51 | @go run k8s.io/code-generator/cmd/informer-gen \ 52 | --go-header-file hack/boilerplate/boilerplate.go.txt \ 53 | --input-dirs $(subst $(space),$(comma),$(api-import-paths)) \ 54 | --output-package $(PKG)/pkg/client/generated/informers \ 55 | --versioned-clientset-package $(PKG)/pkg/client/generated/clientset/internalclientset \ 56 | --listers-package $(PKG)/pkg/client/generated/listers 57 | 58 | injection: 59 | @echo "+ Generating injection for $(API_GROUPS)" 60 | @rm -rf pkg/client/generated/injection 61 | @go run knative.dev/pkg/codegen/cmd/injection-gen \ 62 | --go-header-file hack/boilerplate/boilerplate.go.txt \ 63 | --input-dirs $(subst $(space),$(comma),$(api-import-paths)) \ 64 | --output-package $(PKG)/pkg/client/generated/injection \ 65 | --versioned-clientset-package $(PKG)/pkg/client/generated/clientset/internalclientset \ 66 | --listers-package $(PKG)/pkg/client/generated/listers \ 67 | --external-versions-informers-package $(PKG)/pkg/client/generated/informers/externalversions 68 | -------------------------------------------------------------------------------- /hack/release-notes.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | RELEASE=${1:-${GIT_TAG}} 4 | RELEASE=${RELEASE:-${CIRCLE_TAG}} 5 | 6 | if [ -z "${RELEASE}" ]; then 7 | echo "Usage:" 8 | echo "release-notes.sh VERSION" 9 | exit 1 10 | fi 11 | 12 | if ! git rev-list ${RELEASE} >/dev/null 2>&1; then 13 | echo "${RELEASE} does not exist" 14 | exit 15 | fi 16 | 17 | KREPO="bumblebee" 18 | BASE_URL="https://github.com/triggermesh/${KREPO}/releases/download/${RELEASE}" 19 | PREV_RELEASE=${PREV_RELEASE:-$(git describe --tags --abbrev=0 ${RELEASE}^ 2>/dev/null)} 20 | PREV_RELEASE=${PREV_RELEASE:-$(git rev-list --max-parents=0 ${RELEASE}^ 2>/dev/null)} 21 | NOTABLE_CHANGES=$(git cat-file -p ${RELEASE} | sed '/-----BEGIN PGP SIGNATURE-----/,//d' | tail -n +6) 22 | CHANGELOG=$(git log --no-merges --pretty=format:'- [%h] %s (%aN)' ${PREV_RELEASE}..${RELEASE}) 23 | if [ $? -ne 0 ]; then 24 | echo "Error creating changelog" 25 | exit 1 26 | fi 27 | 28 | COMMANDS=$(sed -n -e "s/^\(COMMANDS[[:space:]]*=[[:space:]]*\)\(.*\)$/\2/p" Makefile) 29 | PLATFORMS=$(sed -n -e "s/^\(TARGETS[[:space:]]*?=[[:space:]]*\)\(.*\)$/\2/p" Makefile) 30 | RELEASE_ASSETS_TABLE=$( 31 | echo -n "|"; for command in ${COMMANDS}; do echo -n " ${command} |"; done ; echo 32 | echo -n "|"; for command in ${COMMANDS}; do echo -n "--|"; done ; echo 33 | echo -n "|" 34 | for command in ${COMMANDS}; do 35 | echo -n " ([container](https://gcr.io/triggermesh/${command}:${RELEASE}))" 36 | for platform in ${PLATFORMS}; do 37 | echo -n " ([${platform}](${BASE_URL}/${command}-${platform%/*}-${platform#*/}))" 38 | done 39 | echo -n " |" 40 | done 41 | echo 42 | ) 43 | 44 | cat < 0 { 61 | if configShallowCopy.Burst <= 0 { 62 | return nil, fmt.Errorf("burst is required to be greater than 0 when RateLimiter is not set and QPS is set to greater than 0") 63 | } 64 | configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst) 65 | } 66 | var cs Clientset 67 | var err error 68 | cs.flowV1alpha1, err = flowv1alpha1.NewForConfig(&configShallowCopy) 69 | if err != nil { 70 | return nil, err 71 | } 72 | 73 | cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy) 74 | if err != nil { 75 | return nil, err 76 | } 77 | return &cs, nil 78 | } 79 | 80 | // NewForConfigOrDie creates a new Clientset for the given config and 81 | // panics if there is an error in the config. 82 | func NewForConfigOrDie(c *rest.Config) *Clientset { 83 | var cs Clientset 84 | cs.flowV1alpha1 = flowv1alpha1.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.flowV1alpha1 = flowv1alpha1.New(c) 94 | 95 | cs.DiscoveryClient = discovery.NewDiscoveryClient(c) 96 | return &cs 97 | } 98 | -------------------------------------------------------------------------------- /pkg/client/generated/clientset/internalclientset/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 TriggerMesh Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | // This package has the automatically generated clientset. 20 | package internalclientset 21 | -------------------------------------------------------------------------------- /pkg/client/generated/clientset/internalclientset/fake/clientset_generated.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 TriggerMesh Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | package fake 20 | 21 | import ( 22 | clientset "github.com/triggermesh/bumblebee/pkg/client/generated/clientset/internalclientset" 23 | flowv1alpha1 "github.com/triggermesh/bumblebee/pkg/client/generated/clientset/internalclientset/typed/transformation/v1alpha1" 24 | fakeflowv1alpha1 "github.com/triggermesh/bumblebee/pkg/client/generated/clientset/internalclientset/typed/transformation/v1alpha1/fake" 25 | "k8s.io/apimachinery/pkg/runtime" 26 | "k8s.io/apimachinery/pkg/watch" 27 | "k8s.io/client-go/discovery" 28 | fakediscovery "k8s.io/client-go/discovery/fake" 29 | "k8s.io/client-go/testing" 30 | ) 31 | 32 | // NewSimpleClientset returns a clientset that will respond with the provided objects. 33 | // It's backed by a very simple object tracker that processes creates, updates and deletions as-is, 34 | // without applying any validations and/or defaults. It shouldn't be considered a replacement 35 | // for a real clientset and is mostly useful in simple unit tests. 36 | func NewSimpleClientset(objects ...runtime.Object) *Clientset { 37 | o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder()) 38 | for _, obj := range objects { 39 | if err := o.Add(obj); err != nil { 40 | panic(err) 41 | } 42 | } 43 | 44 | cs := &Clientset{tracker: o} 45 | cs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake} 46 | cs.AddReactor("*", "*", testing.ObjectReaction(o)) 47 | cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) { 48 | gvr := action.GetResource() 49 | ns := action.GetNamespace() 50 | watch, err := o.Watch(gvr, ns) 51 | if err != nil { 52 | return false, nil, err 53 | } 54 | return true, watch, nil 55 | }) 56 | 57 | return cs 58 | } 59 | 60 | // Clientset implements clientset.Interface. Meant to be embedded into a 61 | // struct to get a default implementation. This makes faking out just the method 62 | // you want to test easier. 63 | type Clientset struct { 64 | testing.Fake 65 | discovery *fakediscovery.FakeDiscovery 66 | tracker testing.ObjectTracker 67 | } 68 | 69 | func (c *Clientset) Discovery() discovery.DiscoveryInterface { 70 | return c.discovery 71 | } 72 | 73 | func (c *Clientset) Tracker() testing.ObjectTracker { 74 | return c.tracker 75 | } 76 | 77 | var _ clientset.Interface = &Clientset{} 78 | 79 | // FlowV1alpha1 retrieves the FlowV1alpha1Client 80 | func (c *Clientset) FlowV1alpha1() flowv1alpha1.FlowV1alpha1Interface { 81 | return &fakeflowv1alpha1.FakeFlowV1alpha1{Fake: &c.Fake} 82 | } 83 | -------------------------------------------------------------------------------- /pkg/client/generated/clientset/internalclientset/fake/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 TriggerMesh Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | // This package has the automatically generated fake clientset. 20 | package fake 21 | -------------------------------------------------------------------------------- /pkg/client/generated/clientset/internalclientset/fake/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 TriggerMesh Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | package fake 20 | 21 | import ( 22 | flowv1alpha1 "github.com/triggermesh/bumblebee/pkg/apis/transformation/v1alpha1" 23 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 24 | runtime "k8s.io/apimachinery/pkg/runtime" 25 | schema "k8s.io/apimachinery/pkg/runtime/schema" 26 | serializer "k8s.io/apimachinery/pkg/runtime/serializer" 27 | utilruntime "k8s.io/apimachinery/pkg/util/runtime" 28 | ) 29 | 30 | var scheme = runtime.NewScheme() 31 | var codecs = serializer.NewCodecFactory(scheme) 32 | 33 | var localSchemeBuilder = runtime.SchemeBuilder{ 34 | flowv1alpha1.AddToScheme, 35 | } 36 | 37 | // AddToScheme adds all types of this clientset into the given scheme. This allows composition 38 | // of clientsets, like in: 39 | // 40 | // import ( 41 | // "k8s.io/client-go/kubernetes" 42 | // clientsetscheme "k8s.io/client-go/kubernetes/scheme" 43 | // aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" 44 | // ) 45 | // 46 | // kclientset, _ := kubernetes.NewForConfig(c) 47 | // _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) 48 | // 49 | // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types 50 | // correctly. 51 | var AddToScheme = localSchemeBuilder.AddToScheme 52 | 53 | func init() { 54 | v1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"}) 55 | utilruntime.Must(AddToScheme(scheme)) 56 | } 57 | -------------------------------------------------------------------------------- /pkg/client/generated/clientset/internalclientset/scheme/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 TriggerMesh Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | // This package contains the scheme of the automatically generated clientset. 20 | package scheme 21 | -------------------------------------------------------------------------------- /pkg/client/generated/clientset/internalclientset/scheme/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 TriggerMesh Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | package scheme 20 | 21 | import ( 22 | flowv1alpha1 "github.com/triggermesh/bumblebee/pkg/apis/transformation/v1alpha1" 23 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 24 | runtime "k8s.io/apimachinery/pkg/runtime" 25 | schema "k8s.io/apimachinery/pkg/runtime/schema" 26 | serializer "k8s.io/apimachinery/pkg/runtime/serializer" 27 | utilruntime "k8s.io/apimachinery/pkg/util/runtime" 28 | ) 29 | 30 | var Scheme = runtime.NewScheme() 31 | var Codecs = serializer.NewCodecFactory(Scheme) 32 | var ParameterCodec = runtime.NewParameterCodec(Scheme) 33 | var localSchemeBuilder = runtime.SchemeBuilder{ 34 | flowv1alpha1.AddToScheme, 35 | } 36 | 37 | // AddToScheme adds all types of this clientset into the given scheme. This allows composition 38 | // of clientsets, like in: 39 | // 40 | // import ( 41 | // "k8s.io/client-go/kubernetes" 42 | // clientsetscheme "k8s.io/client-go/kubernetes/scheme" 43 | // aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" 44 | // ) 45 | // 46 | // kclientset, _ := kubernetes.NewForConfig(c) 47 | // _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) 48 | // 49 | // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types 50 | // correctly. 51 | var AddToScheme = localSchemeBuilder.AddToScheme 52 | 53 | func init() { 54 | v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"}) 55 | utilruntime.Must(AddToScheme(Scheme)) 56 | } 57 | -------------------------------------------------------------------------------- /pkg/client/generated/clientset/internalclientset/typed/transformation/v1alpha1/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 TriggerMesh Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | // This package has the automatically generated typed clients. 20 | package v1alpha1 21 | -------------------------------------------------------------------------------- /pkg/client/generated/clientset/internalclientset/typed/transformation/v1alpha1/fake/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 TriggerMesh Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | // Package fake has the automatically generated clients. 20 | package fake 21 | -------------------------------------------------------------------------------- /pkg/client/generated/clientset/internalclientset/typed/transformation/v1alpha1/fake/fake_transformation.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 TriggerMesh Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | package fake 20 | 21 | import ( 22 | "context" 23 | 24 | v1alpha1 "github.com/triggermesh/bumblebee/pkg/apis/transformation/v1alpha1" 25 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 | labels "k8s.io/apimachinery/pkg/labels" 27 | schema "k8s.io/apimachinery/pkg/runtime/schema" 28 | types "k8s.io/apimachinery/pkg/types" 29 | watch "k8s.io/apimachinery/pkg/watch" 30 | testing "k8s.io/client-go/testing" 31 | ) 32 | 33 | // FakeTransformations implements TransformationInterface 34 | type FakeTransformations struct { 35 | Fake *FakeFlowV1alpha1 36 | ns string 37 | } 38 | 39 | var transformationsResource = schema.GroupVersionResource{Group: "flow.triggermesh.io", Version: "v1alpha1", Resource: "transformations"} 40 | 41 | var transformationsKind = schema.GroupVersionKind{Group: "flow.triggermesh.io", Version: "v1alpha1", Kind: "Transformation"} 42 | 43 | // Get takes name of the transformation, and returns the corresponding transformation object, and an error if there is any. 44 | func (c *FakeTransformations) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.Transformation, err error) { 45 | obj, err := c.Fake. 46 | Invokes(testing.NewGetAction(transformationsResource, c.ns, name), &v1alpha1.Transformation{}) 47 | 48 | if obj == nil { 49 | return nil, err 50 | } 51 | return obj.(*v1alpha1.Transformation), err 52 | } 53 | 54 | // List takes label and field selectors, and returns the list of Transformations that match those selectors. 55 | func (c *FakeTransformations) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.TransformationList, err error) { 56 | obj, err := c.Fake. 57 | Invokes(testing.NewListAction(transformationsResource, transformationsKind, c.ns, opts), &v1alpha1.TransformationList{}) 58 | 59 | if obj == nil { 60 | return nil, err 61 | } 62 | 63 | label, _, _ := testing.ExtractFromListOptions(opts) 64 | if label == nil { 65 | label = labels.Everything() 66 | } 67 | list := &v1alpha1.TransformationList{ListMeta: obj.(*v1alpha1.TransformationList).ListMeta} 68 | for _, item := range obj.(*v1alpha1.TransformationList).Items { 69 | if label.Matches(labels.Set(item.Labels)) { 70 | list.Items = append(list.Items, item) 71 | } 72 | } 73 | return list, err 74 | } 75 | 76 | // Watch returns a watch.Interface that watches the requested transformations. 77 | func (c *FakeTransformations) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { 78 | return c.Fake. 79 | InvokesWatch(testing.NewWatchAction(transformationsResource, c.ns, opts)) 80 | 81 | } 82 | 83 | // Create takes the representation of a transformation and creates it. Returns the server's representation of the transformation, and an error, if there is any. 84 | func (c *FakeTransformations) Create(ctx context.Context, transformation *v1alpha1.Transformation, opts v1.CreateOptions) (result *v1alpha1.Transformation, err error) { 85 | obj, err := c.Fake. 86 | Invokes(testing.NewCreateAction(transformationsResource, c.ns, transformation), &v1alpha1.Transformation{}) 87 | 88 | if obj == nil { 89 | return nil, err 90 | } 91 | return obj.(*v1alpha1.Transformation), err 92 | } 93 | 94 | // Update takes the representation of a transformation and updates it. Returns the server's representation of the transformation, and an error, if there is any. 95 | func (c *FakeTransformations) Update(ctx context.Context, transformation *v1alpha1.Transformation, opts v1.UpdateOptions) (result *v1alpha1.Transformation, err error) { 96 | obj, err := c.Fake. 97 | Invokes(testing.NewUpdateAction(transformationsResource, c.ns, transformation), &v1alpha1.Transformation{}) 98 | 99 | if obj == nil { 100 | return nil, err 101 | } 102 | return obj.(*v1alpha1.Transformation), err 103 | } 104 | 105 | // UpdateStatus was generated because the type contains a Status member. 106 | // Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). 107 | func (c *FakeTransformations) UpdateStatus(ctx context.Context, transformation *v1alpha1.Transformation, opts v1.UpdateOptions) (*v1alpha1.Transformation, error) { 108 | obj, err := c.Fake. 109 | Invokes(testing.NewUpdateSubresourceAction(transformationsResource, "status", c.ns, transformation), &v1alpha1.Transformation{}) 110 | 111 | if obj == nil { 112 | return nil, err 113 | } 114 | return obj.(*v1alpha1.Transformation), err 115 | } 116 | 117 | // Delete takes name of the transformation and deletes it. Returns an error if one occurs. 118 | func (c *FakeTransformations) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { 119 | _, err := c.Fake. 120 | Invokes(testing.NewDeleteAction(transformationsResource, c.ns, name), &v1alpha1.Transformation{}) 121 | 122 | return err 123 | } 124 | 125 | // DeleteCollection deletes a collection of objects. 126 | func (c *FakeTransformations) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { 127 | action := testing.NewDeleteCollectionAction(transformationsResource, c.ns, listOpts) 128 | 129 | _, err := c.Fake.Invokes(action, &v1alpha1.TransformationList{}) 130 | return err 131 | } 132 | 133 | // Patch applies the patch and returns the patched transformation. 134 | func (c *FakeTransformations) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.Transformation, err error) { 135 | obj, err := c.Fake. 136 | Invokes(testing.NewPatchSubresourceAction(transformationsResource, c.ns, name, pt, data, subresources...), &v1alpha1.Transformation{}) 137 | 138 | if obj == nil { 139 | return nil, err 140 | } 141 | return obj.(*v1alpha1.Transformation), err 142 | } 143 | -------------------------------------------------------------------------------- /pkg/client/generated/clientset/internalclientset/typed/transformation/v1alpha1/fake/fake_transformation_client.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 TriggerMesh Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | package fake 20 | 21 | import ( 22 | v1alpha1 "github.com/triggermesh/bumblebee/pkg/client/generated/clientset/internalclientset/typed/transformation/v1alpha1" 23 | rest "k8s.io/client-go/rest" 24 | testing "k8s.io/client-go/testing" 25 | ) 26 | 27 | type FakeFlowV1alpha1 struct { 28 | *testing.Fake 29 | } 30 | 31 | func (c *FakeFlowV1alpha1) Transformations(namespace string) v1alpha1.TransformationInterface { 32 | return &FakeTransformations{c, namespace} 33 | } 34 | 35 | // RESTClient returns a RESTClient that is used to communicate 36 | // with API server by this client implementation. 37 | func (c *FakeFlowV1alpha1) RESTClient() rest.Interface { 38 | var ret *rest.RESTClient 39 | return ret 40 | } 41 | -------------------------------------------------------------------------------- /pkg/client/generated/clientset/internalclientset/typed/transformation/v1alpha1/generated_expansion.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 TriggerMesh Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | package v1alpha1 20 | 21 | type TransformationExpansion interface{} 22 | -------------------------------------------------------------------------------- /pkg/client/generated/clientset/internalclientset/typed/transformation/v1alpha1/transformation_client.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 TriggerMesh Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | package v1alpha1 20 | 21 | import ( 22 | v1alpha1 "github.com/triggermesh/bumblebee/pkg/apis/transformation/v1alpha1" 23 | "github.com/triggermesh/bumblebee/pkg/client/generated/clientset/internalclientset/scheme" 24 | rest "k8s.io/client-go/rest" 25 | ) 26 | 27 | type FlowV1alpha1Interface interface { 28 | RESTClient() rest.Interface 29 | TransformationsGetter 30 | } 31 | 32 | // FlowV1alpha1Client is used to interact with features provided by the flow.triggermesh.io group. 33 | type FlowV1alpha1Client struct { 34 | restClient rest.Interface 35 | } 36 | 37 | func (c *FlowV1alpha1Client) Transformations(namespace string) TransformationInterface { 38 | return newTransformations(c, namespace) 39 | } 40 | 41 | // NewForConfig creates a new FlowV1alpha1Client for the given config. 42 | func NewForConfig(c *rest.Config) (*FlowV1alpha1Client, error) { 43 | config := *c 44 | if err := setConfigDefaults(&config); err != nil { 45 | return nil, err 46 | } 47 | client, err := rest.RESTClientFor(&config) 48 | if err != nil { 49 | return nil, err 50 | } 51 | return &FlowV1alpha1Client{client}, nil 52 | } 53 | 54 | // NewForConfigOrDie creates a new FlowV1alpha1Client for the given config and 55 | // panics if there is an error in the config. 56 | func NewForConfigOrDie(c *rest.Config) *FlowV1alpha1Client { 57 | client, err := NewForConfig(c) 58 | if err != nil { 59 | panic(err) 60 | } 61 | return client 62 | } 63 | 64 | // New creates a new FlowV1alpha1Client for the given RESTClient. 65 | func New(c rest.Interface) *FlowV1alpha1Client { 66 | return &FlowV1alpha1Client{c} 67 | } 68 | 69 | func setConfigDefaults(config *rest.Config) error { 70 | gv := v1alpha1.SchemeGroupVersion 71 | config.GroupVersion = &gv 72 | config.APIPath = "/apis" 73 | config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() 74 | 75 | if config.UserAgent == "" { 76 | config.UserAgent = rest.DefaultKubernetesUserAgent() 77 | } 78 | 79 | return nil 80 | } 81 | 82 | // RESTClient returns a RESTClient that is used to communicate 83 | // with API server by this client implementation. 84 | func (c *FlowV1alpha1Client) RESTClient() rest.Interface { 85 | if c == nil { 86 | return nil 87 | } 88 | return c.restClient 89 | } 90 | -------------------------------------------------------------------------------- /pkg/client/generated/informers/externalversions/factory.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 TriggerMesh Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by informer-gen. DO NOT EDIT. 18 | 19 | package externalversions 20 | 21 | import ( 22 | reflect "reflect" 23 | sync "sync" 24 | time "time" 25 | 26 | internalclientset "github.com/triggermesh/bumblebee/pkg/client/generated/clientset/internalclientset" 27 | internalinterfaces "github.com/triggermesh/bumblebee/pkg/client/generated/informers/externalversions/internalinterfaces" 28 | transformation "github.com/triggermesh/bumblebee/pkg/client/generated/informers/externalversions/transformation" 29 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 30 | runtime "k8s.io/apimachinery/pkg/runtime" 31 | schema "k8s.io/apimachinery/pkg/runtime/schema" 32 | cache "k8s.io/client-go/tools/cache" 33 | ) 34 | 35 | // SharedInformerOption defines the functional option type for SharedInformerFactory. 36 | type SharedInformerOption func(*sharedInformerFactory) *sharedInformerFactory 37 | 38 | type sharedInformerFactory struct { 39 | client internalclientset.Interface 40 | namespace string 41 | tweakListOptions internalinterfaces.TweakListOptionsFunc 42 | lock sync.Mutex 43 | defaultResync time.Duration 44 | customResync map[reflect.Type]time.Duration 45 | 46 | informers map[reflect.Type]cache.SharedIndexInformer 47 | // startedInformers is used for tracking which informers have been started. 48 | // This allows Start() to be called multiple times safely. 49 | startedInformers map[reflect.Type]bool 50 | } 51 | 52 | // WithCustomResyncConfig sets a custom resync period for the specified informer types. 53 | func WithCustomResyncConfig(resyncConfig map[v1.Object]time.Duration) SharedInformerOption { 54 | return func(factory *sharedInformerFactory) *sharedInformerFactory { 55 | for k, v := range resyncConfig { 56 | factory.customResync[reflect.TypeOf(k)] = v 57 | } 58 | return factory 59 | } 60 | } 61 | 62 | // WithTweakListOptions sets a custom filter on all listers of the configured SharedInformerFactory. 63 | func WithTweakListOptions(tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerOption { 64 | return func(factory *sharedInformerFactory) *sharedInformerFactory { 65 | factory.tweakListOptions = tweakListOptions 66 | return factory 67 | } 68 | } 69 | 70 | // WithNamespace limits the SharedInformerFactory to the specified namespace. 71 | func WithNamespace(namespace string) SharedInformerOption { 72 | return func(factory *sharedInformerFactory) *sharedInformerFactory { 73 | factory.namespace = namespace 74 | return factory 75 | } 76 | } 77 | 78 | // NewSharedInformerFactory constructs a new instance of sharedInformerFactory for all namespaces. 79 | func NewSharedInformerFactory(client internalclientset.Interface, defaultResync time.Duration) SharedInformerFactory { 80 | return NewSharedInformerFactoryWithOptions(client, defaultResync) 81 | } 82 | 83 | // NewFilteredSharedInformerFactory constructs a new instance of sharedInformerFactory. 84 | // Listers obtained via this SharedInformerFactory will be subject to the same filters 85 | // as specified here. 86 | // Deprecated: Please use NewSharedInformerFactoryWithOptions instead 87 | func NewFilteredSharedInformerFactory(client internalclientset.Interface, defaultResync time.Duration, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerFactory { 88 | return NewSharedInformerFactoryWithOptions(client, defaultResync, WithNamespace(namespace), WithTweakListOptions(tweakListOptions)) 89 | } 90 | 91 | // NewSharedInformerFactoryWithOptions constructs a new instance of a SharedInformerFactory with additional options. 92 | func NewSharedInformerFactoryWithOptions(client internalclientset.Interface, defaultResync time.Duration, options ...SharedInformerOption) SharedInformerFactory { 93 | factory := &sharedInformerFactory{ 94 | client: client, 95 | namespace: v1.NamespaceAll, 96 | defaultResync: defaultResync, 97 | informers: make(map[reflect.Type]cache.SharedIndexInformer), 98 | startedInformers: make(map[reflect.Type]bool), 99 | customResync: make(map[reflect.Type]time.Duration), 100 | } 101 | 102 | // Apply all options 103 | for _, opt := range options { 104 | factory = opt(factory) 105 | } 106 | 107 | return factory 108 | } 109 | 110 | // Start initializes all requested informers. 111 | func (f *sharedInformerFactory) Start(stopCh <-chan struct{}) { 112 | f.lock.Lock() 113 | defer f.lock.Unlock() 114 | 115 | for informerType, informer := range f.informers { 116 | if !f.startedInformers[informerType] { 117 | go informer.Run(stopCh) 118 | f.startedInformers[informerType] = true 119 | } 120 | } 121 | } 122 | 123 | // WaitForCacheSync waits for all started informers' cache were synced. 124 | func (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool { 125 | informers := func() map[reflect.Type]cache.SharedIndexInformer { 126 | f.lock.Lock() 127 | defer f.lock.Unlock() 128 | 129 | informers := map[reflect.Type]cache.SharedIndexInformer{} 130 | for informerType, informer := range f.informers { 131 | if f.startedInformers[informerType] { 132 | informers[informerType] = informer 133 | } 134 | } 135 | return informers 136 | }() 137 | 138 | res := map[reflect.Type]bool{} 139 | for informType, informer := range informers { 140 | res[informType] = cache.WaitForCacheSync(stopCh, informer.HasSynced) 141 | } 142 | return res 143 | } 144 | 145 | // InternalInformerFor returns the SharedIndexInformer for obj using an internal 146 | // client. 147 | func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer { 148 | f.lock.Lock() 149 | defer f.lock.Unlock() 150 | 151 | informerType := reflect.TypeOf(obj) 152 | informer, exists := f.informers[informerType] 153 | if exists { 154 | return informer 155 | } 156 | 157 | resyncPeriod, exists := f.customResync[informerType] 158 | if !exists { 159 | resyncPeriod = f.defaultResync 160 | } 161 | 162 | informer = newFunc(f.client, resyncPeriod) 163 | f.informers[informerType] = informer 164 | 165 | return informer 166 | } 167 | 168 | // SharedInformerFactory provides shared informers for resources in all known 169 | // API group versions. 170 | type SharedInformerFactory interface { 171 | internalinterfaces.SharedInformerFactory 172 | ForResource(resource schema.GroupVersionResource) (GenericInformer, error) 173 | WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool 174 | 175 | Flow() transformation.Interface 176 | } 177 | 178 | func (f *sharedInformerFactory) Flow() transformation.Interface { 179 | return transformation.New(f, f.namespace, f.tweakListOptions) 180 | } 181 | -------------------------------------------------------------------------------- /pkg/client/generated/informers/externalversions/generic.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 TriggerMesh Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by informer-gen. DO NOT EDIT. 18 | 19 | package externalversions 20 | 21 | import ( 22 | "fmt" 23 | 24 | v1alpha1 "github.com/triggermesh/bumblebee/pkg/apis/transformation/v1alpha1" 25 | schema "k8s.io/apimachinery/pkg/runtime/schema" 26 | cache "k8s.io/client-go/tools/cache" 27 | ) 28 | 29 | // GenericInformer is type of SharedIndexInformer which will locate and delegate to other 30 | // sharedInformers based on type 31 | type GenericInformer interface { 32 | Informer() cache.SharedIndexInformer 33 | Lister() cache.GenericLister 34 | } 35 | 36 | type genericInformer struct { 37 | informer cache.SharedIndexInformer 38 | resource schema.GroupResource 39 | } 40 | 41 | // Informer returns the SharedIndexInformer. 42 | func (f *genericInformer) Informer() cache.SharedIndexInformer { 43 | return f.informer 44 | } 45 | 46 | // Lister returns the GenericLister. 47 | func (f *genericInformer) Lister() cache.GenericLister { 48 | return cache.NewGenericLister(f.Informer().GetIndexer(), f.resource) 49 | } 50 | 51 | // ForResource gives generic access to a shared informer of the matching type 52 | // TODO extend this to unknown resources with a client pool 53 | func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) { 54 | switch resource { 55 | // Group=flow.triggermesh.io, Version=v1alpha1 56 | case v1alpha1.SchemeGroupVersion.WithResource("transformations"): 57 | return &genericInformer{resource: resource.GroupResource(), informer: f.Flow().V1alpha1().Transformations().Informer()}, nil 58 | 59 | } 60 | 61 | return nil, fmt.Errorf("no informer found for %v", resource) 62 | } 63 | -------------------------------------------------------------------------------- /pkg/client/generated/informers/externalversions/internalinterfaces/factory_interfaces.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 TriggerMesh Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by informer-gen. DO NOT EDIT. 18 | 19 | package internalinterfaces 20 | 21 | import ( 22 | time "time" 23 | 24 | internalclientset "github.com/triggermesh/bumblebee/pkg/client/generated/clientset/internalclientset" 25 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 | runtime "k8s.io/apimachinery/pkg/runtime" 27 | cache "k8s.io/client-go/tools/cache" 28 | ) 29 | 30 | // NewInformerFunc takes internalclientset.Interface and time.Duration to return a SharedIndexInformer. 31 | type NewInformerFunc func(internalclientset.Interface, time.Duration) cache.SharedIndexInformer 32 | 33 | // SharedInformerFactory a small interface to allow for adding an informer without an import cycle 34 | type SharedInformerFactory interface { 35 | Start(stopCh <-chan struct{}) 36 | InformerFor(obj runtime.Object, newFunc NewInformerFunc) cache.SharedIndexInformer 37 | } 38 | 39 | // TweakListOptionsFunc is a function that transforms a v1.ListOptions. 40 | type TweakListOptionsFunc func(*v1.ListOptions) 41 | -------------------------------------------------------------------------------- /pkg/client/generated/informers/externalversions/transformation/interface.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 TriggerMesh Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by informer-gen. DO NOT EDIT. 18 | 19 | package transformation 20 | 21 | import ( 22 | internalinterfaces "github.com/triggermesh/bumblebee/pkg/client/generated/informers/externalversions/internalinterfaces" 23 | v1alpha1 "github.com/triggermesh/bumblebee/pkg/client/generated/informers/externalversions/transformation/v1alpha1" 24 | ) 25 | 26 | // Interface provides access to each of this group's versions. 27 | type Interface interface { 28 | // V1alpha1 provides access to shared informers for resources in V1alpha1. 29 | V1alpha1() v1alpha1.Interface 30 | } 31 | 32 | type group struct { 33 | factory internalinterfaces.SharedInformerFactory 34 | namespace string 35 | tweakListOptions internalinterfaces.TweakListOptionsFunc 36 | } 37 | 38 | // New returns a new Interface. 39 | func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { 40 | return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} 41 | } 42 | 43 | // V1alpha1 returns a new v1alpha1.Interface. 44 | func (g *group) V1alpha1() v1alpha1.Interface { 45 | return v1alpha1.New(g.factory, g.namespace, g.tweakListOptions) 46 | } 47 | -------------------------------------------------------------------------------- /pkg/client/generated/informers/externalversions/transformation/v1alpha1/interface.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 TriggerMesh Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by informer-gen. DO NOT EDIT. 18 | 19 | package v1alpha1 20 | 21 | import ( 22 | internalinterfaces "github.com/triggermesh/bumblebee/pkg/client/generated/informers/externalversions/internalinterfaces" 23 | ) 24 | 25 | // Interface provides access to all the informers in this group version. 26 | type Interface interface { 27 | // Transformations returns a TransformationInformer. 28 | Transformations() TransformationInformer 29 | } 30 | 31 | type version struct { 32 | factory internalinterfaces.SharedInformerFactory 33 | namespace string 34 | tweakListOptions internalinterfaces.TweakListOptionsFunc 35 | } 36 | 37 | // New returns a new Interface. 38 | func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { 39 | return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} 40 | } 41 | 42 | // Transformations returns a TransformationInformer. 43 | func (v *version) Transformations() TransformationInformer { 44 | return &transformationInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} 45 | } 46 | -------------------------------------------------------------------------------- /pkg/client/generated/informers/externalversions/transformation/v1alpha1/transformation.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 TriggerMesh Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by informer-gen. DO NOT EDIT. 18 | 19 | package v1alpha1 20 | 21 | import ( 22 | "context" 23 | time "time" 24 | 25 | transformationv1alpha1 "github.com/triggermesh/bumblebee/pkg/apis/transformation/v1alpha1" 26 | internalclientset "github.com/triggermesh/bumblebee/pkg/client/generated/clientset/internalclientset" 27 | internalinterfaces "github.com/triggermesh/bumblebee/pkg/client/generated/informers/externalversions/internalinterfaces" 28 | v1alpha1 "github.com/triggermesh/bumblebee/pkg/client/generated/listers/transformation/v1alpha1" 29 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 30 | runtime "k8s.io/apimachinery/pkg/runtime" 31 | watch "k8s.io/apimachinery/pkg/watch" 32 | cache "k8s.io/client-go/tools/cache" 33 | ) 34 | 35 | // TransformationInformer provides access to a shared informer and lister for 36 | // Transformations. 37 | type TransformationInformer interface { 38 | Informer() cache.SharedIndexInformer 39 | Lister() v1alpha1.TransformationLister 40 | } 41 | 42 | type transformationInformer struct { 43 | factory internalinterfaces.SharedInformerFactory 44 | tweakListOptions internalinterfaces.TweakListOptionsFunc 45 | namespace string 46 | } 47 | 48 | // NewTransformationInformer constructs a new informer for Transformation type. 49 | // Always prefer using an informer factory to get a shared informer instead of getting an independent 50 | // one. This reduces memory footprint and number of connections to the server. 51 | func NewTransformationInformer(client internalclientset.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { 52 | return NewFilteredTransformationInformer(client, namespace, resyncPeriod, indexers, nil) 53 | } 54 | 55 | // NewFilteredTransformationInformer constructs a new informer for Transformation type. 56 | // Always prefer using an informer factory to get a shared informer instead of getting an independent 57 | // one. This reduces memory footprint and number of connections to the server. 58 | func NewFilteredTransformationInformer(client internalclientset.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { 59 | return cache.NewSharedIndexInformer( 60 | &cache.ListWatch{ 61 | ListFunc: func(options v1.ListOptions) (runtime.Object, error) { 62 | if tweakListOptions != nil { 63 | tweakListOptions(&options) 64 | } 65 | return client.FlowV1alpha1().Transformations(namespace).List(context.TODO(), options) 66 | }, 67 | WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { 68 | if tweakListOptions != nil { 69 | tweakListOptions(&options) 70 | } 71 | return client.FlowV1alpha1().Transformations(namespace).Watch(context.TODO(), options) 72 | }, 73 | }, 74 | &transformationv1alpha1.Transformation{}, 75 | resyncPeriod, 76 | indexers, 77 | ) 78 | } 79 | 80 | func (f *transformationInformer) defaultInformer(client internalclientset.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { 81 | return NewFilteredTransformationInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) 82 | } 83 | 84 | func (f *transformationInformer) Informer() cache.SharedIndexInformer { 85 | return f.factory.InformerFor(&transformationv1alpha1.Transformation{}, f.defaultInformer) 86 | } 87 | 88 | func (f *transformationInformer) Lister() v1alpha1.TransformationLister { 89 | return v1alpha1.NewTransformationLister(f.Informer().GetIndexer()) 90 | } 91 | -------------------------------------------------------------------------------- /pkg/client/generated/injection/client/client.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 TriggerMesh Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by injection-gen. DO NOT EDIT. 18 | 19 | package client 20 | 21 | import ( 22 | context "context" 23 | 24 | internalclientset "github.com/triggermesh/bumblebee/pkg/client/generated/clientset/internalclientset" 25 | rest "k8s.io/client-go/rest" 26 | injection "knative.dev/pkg/injection" 27 | logging "knative.dev/pkg/logging" 28 | ) 29 | 30 | func init() { 31 | injection.Default.RegisterClient(withClient) 32 | injection.Default.RegisterClientFetcher(func(ctx context.Context) interface{} { 33 | return Get(ctx) 34 | }) 35 | } 36 | 37 | // Key is used as the key for associating information with a context.Context. 38 | type Key struct{} 39 | 40 | func withClient(ctx context.Context, cfg *rest.Config) context.Context { 41 | return context.WithValue(ctx, Key{}, internalclientset.NewForConfigOrDie(cfg)) 42 | } 43 | 44 | // Get extracts the internalclientset.Interface client from the context. 45 | func Get(ctx context.Context) internalclientset.Interface { 46 | untyped := ctx.Value(Key{}) 47 | if untyped == nil { 48 | if injection.GetConfig(ctx) == nil { 49 | logging.FromContext(ctx).Panic( 50 | "Unable to fetch github.com/triggermesh/bumblebee/pkg/client/generated/clientset/internalclientset.Interface from context. This context is not the application context (which is typically given to constructors via sharedmain).") 51 | } else { 52 | logging.FromContext(ctx).Panic( 53 | "Unable to fetch github.com/triggermesh/bumblebee/pkg/client/generated/clientset/internalclientset.Interface from context.") 54 | } 55 | } 56 | return untyped.(internalclientset.Interface) 57 | } 58 | -------------------------------------------------------------------------------- /pkg/client/generated/injection/client/fake/fake.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 TriggerMesh Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by injection-gen. DO NOT EDIT. 18 | 19 | package fake 20 | 21 | import ( 22 | context "context" 23 | 24 | fake "github.com/triggermesh/bumblebee/pkg/client/generated/clientset/internalclientset/fake" 25 | client "github.com/triggermesh/bumblebee/pkg/client/generated/injection/client" 26 | runtime "k8s.io/apimachinery/pkg/runtime" 27 | rest "k8s.io/client-go/rest" 28 | injection "knative.dev/pkg/injection" 29 | logging "knative.dev/pkg/logging" 30 | ) 31 | 32 | func init() { 33 | injection.Fake.RegisterClient(withClient) 34 | injection.Fake.RegisterClientFetcher(func(ctx context.Context) interface{} { 35 | return Get(ctx) 36 | }) 37 | } 38 | 39 | func withClient(ctx context.Context, cfg *rest.Config) context.Context { 40 | ctx, _ = With(ctx) 41 | return ctx 42 | } 43 | 44 | func With(ctx context.Context, objects ...runtime.Object) (context.Context, *fake.Clientset) { 45 | cs := fake.NewSimpleClientset(objects...) 46 | return context.WithValue(ctx, client.Key{}, cs), cs 47 | } 48 | 49 | // Get extracts the Kubernetes client from the context. 50 | func Get(ctx context.Context) *fake.Clientset { 51 | untyped := ctx.Value(client.Key{}) 52 | if untyped == nil { 53 | logging.FromContext(ctx).Panic( 54 | "Unable to fetch github.com/triggermesh/bumblebee/pkg/client/generated/clientset/internalclientset/fake.Clientset from context.") 55 | } 56 | return untyped.(*fake.Clientset) 57 | } 58 | -------------------------------------------------------------------------------- /pkg/client/generated/injection/informers/factory/factory.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 TriggerMesh Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by injection-gen. DO NOT EDIT. 18 | 19 | package factory 20 | 21 | import ( 22 | context "context" 23 | 24 | externalversions "github.com/triggermesh/bumblebee/pkg/client/generated/informers/externalversions" 25 | client "github.com/triggermesh/bumblebee/pkg/client/generated/injection/client" 26 | controller "knative.dev/pkg/controller" 27 | injection "knative.dev/pkg/injection" 28 | logging "knative.dev/pkg/logging" 29 | ) 30 | 31 | func init() { 32 | injection.Default.RegisterInformerFactory(withInformerFactory) 33 | } 34 | 35 | // Key is used as the key for associating information with a context.Context. 36 | type Key struct{} 37 | 38 | func withInformerFactory(ctx context.Context) context.Context { 39 | c := client.Get(ctx) 40 | opts := make([]externalversions.SharedInformerOption, 0, 1) 41 | if injection.HasNamespaceScope(ctx) { 42 | opts = append(opts, externalversions.WithNamespace(injection.GetNamespaceScope(ctx))) 43 | } 44 | return context.WithValue(ctx, Key{}, 45 | externalversions.NewSharedInformerFactoryWithOptions(c, controller.GetResyncPeriod(ctx), opts...)) 46 | } 47 | 48 | // Get extracts the InformerFactory from the context. 49 | func Get(ctx context.Context) externalversions.SharedInformerFactory { 50 | untyped := ctx.Value(Key{}) 51 | if untyped == nil { 52 | logging.FromContext(ctx).Panic( 53 | "Unable to fetch github.com/triggermesh/bumblebee/pkg/client/generated/informers/externalversions.SharedInformerFactory from context.") 54 | } 55 | return untyped.(externalversions.SharedInformerFactory) 56 | } 57 | -------------------------------------------------------------------------------- /pkg/client/generated/injection/informers/factory/fake/fake.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 TriggerMesh Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by injection-gen. DO NOT EDIT. 18 | 19 | package fake 20 | 21 | import ( 22 | context "context" 23 | 24 | externalversions "github.com/triggermesh/bumblebee/pkg/client/generated/informers/externalversions" 25 | fake "github.com/triggermesh/bumblebee/pkg/client/generated/injection/client/fake" 26 | factory "github.com/triggermesh/bumblebee/pkg/client/generated/injection/informers/factory" 27 | controller "knative.dev/pkg/controller" 28 | injection "knative.dev/pkg/injection" 29 | ) 30 | 31 | var Get = factory.Get 32 | 33 | func init() { 34 | injection.Fake.RegisterInformerFactory(withInformerFactory) 35 | } 36 | 37 | func withInformerFactory(ctx context.Context) context.Context { 38 | c := fake.Get(ctx) 39 | opts := make([]externalversions.SharedInformerOption, 0, 1) 40 | if injection.HasNamespaceScope(ctx) { 41 | opts = append(opts, externalversions.WithNamespace(injection.GetNamespaceScope(ctx))) 42 | } 43 | return context.WithValue(ctx, factory.Key{}, 44 | externalversions.NewSharedInformerFactoryWithOptions(c, controller.GetResyncPeriod(ctx), opts...)) 45 | } 46 | -------------------------------------------------------------------------------- /pkg/client/generated/injection/informers/factory/filtered/fake/fake_filtered_factory.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 TriggerMesh Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by injection-gen. DO NOT EDIT. 18 | 19 | package fakeFilteredFactory 20 | 21 | import ( 22 | context "context" 23 | 24 | externalversions "github.com/triggermesh/bumblebee/pkg/client/generated/informers/externalversions" 25 | fake "github.com/triggermesh/bumblebee/pkg/client/generated/injection/client/fake" 26 | filtered "github.com/triggermesh/bumblebee/pkg/client/generated/injection/informers/factory/filtered" 27 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 28 | controller "knative.dev/pkg/controller" 29 | injection "knative.dev/pkg/injection" 30 | logging "knative.dev/pkg/logging" 31 | ) 32 | 33 | var Get = filtered.Get 34 | 35 | func init() { 36 | injection.Fake.RegisterInformerFactory(withInformerFactory) 37 | } 38 | 39 | func withInformerFactory(ctx context.Context) context.Context { 40 | c := fake.Get(ctx) 41 | untyped := ctx.Value(filtered.LabelKey{}) 42 | if untyped == nil { 43 | logging.FromContext(ctx).Panic( 44 | "Unable to fetch labelkey from context.") 45 | } 46 | labelSelectors := untyped.([]string) 47 | for _, selector := range labelSelectors { 48 | opts := []externalversions.SharedInformerOption{} 49 | if injection.HasNamespaceScope(ctx) { 50 | opts = append(opts, externalversions.WithNamespace(injection.GetNamespaceScope(ctx))) 51 | } 52 | opts = append(opts, externalversions.WithTweakListOptions(func(l *v1.ListOptions) { 53 | l.LabelSelector = selector 54 | })) 55 | ctx = context.WithValue(ctx, filtered.Key{Selector: selector}, 56 | externalversions.NewSharedInformerFactoryWithOptions(c, controller.GetResyncPeriod(ctx), opts...)) 57 | } 58 | return ctx 59 | } 60 | -------------------------------------------------------------------------------- /pkg/client/generated/injection/informers/factory/filtered/filtered_factory.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 TriggerMesh Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by injection-gen. DO NOT EDIT. 18 | 19 | package filteredFactory 20 | 21 | import ( 22 | context "context" 23 | 24 | externalversions "github.com/triggermesh/bumblebee/pkg/client/generated/informers/externalversions" 25 | client "github.com/triggermesh/bumblebee/pkg/client/generated/injection/client" 26 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 | controller "knative.dev/pkg/controller" 28 | injection "knative.dev/pkg/injection" 29 | logging "knative.dev/pkg/logging" 30 | ) 31 | 32 | func init() { 33 | injection.Default.RegisterInformerFactory(withInformerFactory) 34 | } 35 | 36 | // Key is used as the key for associating information with a context.Context. 37 | type Key struct { 38 | Selector string 39 | } 40 | 41 | type LabelKey struct{} 42 | 43 | func WithSelectors(ctx context.Context, selector ...string) context.Context { 44 | return context.WithValue(ctx, LabelKey{}, selector) 45 | } 46 | 47 | func withInformerFactory(ctx context.Context) context.Context { 48 | c := client.Get(ctx) 49 | untyped := ctx.Value(LabelKey{}) 50 | if untyped == nil { 51 | logging.FromContext(ctx).Panic( 52 | "Unable to fetch labelkey from context.") 53 | } 54 | labelSelectors := untyped.([]string) 55 | for _, selector := range labelSelectors { 56 | opts := []externalversions.SharedInformerOption{} 57 | if injection.HasNamespaceScope(ctx) { 58 | opts = append(opts, externalversions.WithNamespace(injection.GetNamespaceScope(ctx))) 59 | } 60 | opts = append(opts, externalversions.WithTweakListOptions(func(l *v1.ListOptions) { 61 | l.LabelSelector = selector 62 | })) 63 | ctx = context.WithValue(ctx, Key{Selector: selector}, 64 | externalversions.NewSharedInformerFactoryWithOptions(c, controller.GetResyncPeriod(ctx), opts...)) 65 | } 66 | return ctx 67 | } 68 | 69 | // Get extracts the InformerFactory from the context. 70 | func Get(ctx context.Context, selector string) externalversions.SharedInformerFactory { 71 | untyped := ctx.Value(Key{Selector: selector}) 72 | if untyped == nil { 73 | logging.FromContext(ctx).Panicf( 74 | "Unable to fetch github.com/triggermesh/bumblebee/pkg/client/generated/informers/externalversions.SharedInformerFactory with selector %s from context.", selector) 75 | } 76 | return untyped.(externalversions.SharedInformerFactory) 77 | } 78 | -------------------------------------------------------------------------------- /pkg/client/generated/injection/informers/transformation/v1alpha1/transformation/fake/fake.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 TriggerMesh Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by injection-gen. DO NOT EDIT. 18 | 19 | package fake 20 | 21 | import ( 22 | context "context" 23 | 24 | fake "github.com/triggermesh/bumblebee/pkg/client/generated/injection/informers/factory/fake" 25 | transformation "github.com/triggermesh/bumblebee/pkg/client/generated/injection/informers/transformation/v1alpha1/transformation" 26 | controller "knative.dev/pkg/controller" 27 | injection "knative.dev/pkg/injection" 28 | ) 29 | 30 | var Get = transformation.Get 31 | 32 | func init() { 33 | injection.Fake.RegisterInformer(withInformer) 34 | } 35 | 36 | func withInformer(ctx context.Context) (context.Context, controller.Informer) { 37 | f := fake.Get(ctx) 38 | inf := f.Flow().V1alpha1().Transformations() 39 | return context.WithValue(ctx, transformation.Key{}, inf), inf.Informer() 40 | } 41 | -------------------------------------------------------------------------------- /pkg/client/generated/injection/informers/transformation/v1alpha1/transformation/filtered/fake/fake.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 TriggerMesh Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by injection-gen. DO NOT EDIT. 18 | 19 | package fake 20 | 21 | import ( 22 | context "context" 23 | 24 | factoryfiltered "github.com/triggermesh/bumblebee/pkg/client/generated/injection/informers/factory/filtered" 25 | filtered "github.com/triggermesh/bumblebee/pkg/client/generated/injection/informers/transformation/v1alpha1/transformation/filtered" 26 | controller "knative.dev/pkg/controller" 27 | injection "knative.dev/pkg/injection" 28 | logging "knative.dev/pkg/logging" 29 | ) 30 | 31 | var Get = filtered.Get 32 | 33 | func init() { 34 | injection.Fake.RegisterFilteredInformers(withInformer) 35 | } 36 | 37 | func withInformer(ctx context.Context) (context.Context, []controller.Informer) { 38 | untyped := ctx.Value(factoryfiltered.LabelKey{}) 39 | if untyped == nil { 40 | logging.FromContext(ctx).Panic( 41 | "Unable to fetch labelkey from context.") 42 | } 43 | labelSelectors := untyped.([]string) 44 | infs := []controller.Informer{} 45 | for _, selector := range labelSelectors { 46 | f := factoryfiltered.Get(ctx, selector) 47 | inf := f.Flow().V1alpha1().Transformations() 48 | ctx = context.WithValue(ctx, filtered.Key{Selector: selector}, inf) 49 | infs = append(infs, inf.Informer()) 50 | } 51 | return ctx, infs 52 | } 53 | -------------------------------------------------------------------------------- /pkg/client/generated/injection/informers/transformation/v1alpha1/transformation/filtered/transformation.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 TriggerMesh Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by injection-gen. DO NOT EDIT. 18 | 19 | package filtered 20 | 21 | import ( 22 | context "context" 23 | 24 | v1alpha1 "github.com/triggermesh/bumblebee/pkg/client/generated/informers/externalversions/transformation/v1alpha1" 25 | filtered "github.com/triggermesh/bumblebee/pkg/client/generated/injection/informers/factory/filtered" 26 | controller "knative.dev/pkg/controller" 27 | injection "knative.dev/pkg/injection" 28 | logging "knative.dev/pkg/logging" 29 | ) 30 | 31 | func init() { 32 | injection.Default.RegisterFilteredInformers(withInformer) 33 | } 34 | 35 | // Key is used for associating the Informer inside the context.Context. 36 | type Key struct { 37 | Selector string 38 | } 39 | 40 | func withInformer(ctx context.Context) (context.Context, []controller.Informer) { 41 | untyped := ctx.Value(filtered.LabelKey{}) 42 | if untyped == nil { 43 | logging.FromContext(ctx).Panic( 44 | "Unable to fetch labelkey from context.") 45 | } 46 | labelSelectors := untyped.([]string) 47 | infs := []controller.Informer{} 48 | for _, selector := range labelSelectors { 49 | f := filtered.Get(ctx, selector) 50 | inf := f.Flow().V1alpha1().Transformations() 51 | ctx = context.WithValue(ctx, Key{Selector: selector}, inf) 52 | infs = append(infs, inf.Informer()) 53 | } 54 | return ctx, infs 55 | } 56 | 57 | // Get extracts the typed informer from the context. 58 | func Get(ctx context.Context, selector string) v1alpha1.TransformationInformer { 59 | untyped := ctx.Value(Key{Selector: selector}) 60 | if untyped == nil { 61 | logging.FromContext(ctx).Panicf( 62 | "Unable to fetch github.com/triggermesh/bumblebee/pkg/client/generated/informers/externalversions/transformation/v1alpha1.TransformationInformer with selector %s from context.", selector) 63 | } 64 | return untyped.(v1alpha1.TransformationInformer) 65 | } 66 | -------------------------------------------------------------------------------- /pkg/client/generated/injection/informers/transformation/v1alpha1/transformation/transformation.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 TriggerMesh Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by injection-gen. DO NOT EDIT. 18 | 19 | package transformation 20 | 21 | import ( 22 | context "context" 23 | 24 | v1alpha1 "github.com/triggermesh/bumblebee/pkg/client/generated/informers/externalversions/transformation/v1alpha1" 25 | factory "github.com/triggermesh/bumblebee/pkg/client/generated/injection/informers/factory" 26 | controller "knative.dev/pkg/controller" 27 | injection "knative.dev/pkg/injection" 28 | logging "knative.dev/pkg/logging" 29 | ) 30 | 31 | func init() { 32 | injection.Default.RegisterInformer(withInformer) 33 | } 34 | 35 | // Key is used for associating the Informer inside the context.Context. 36 | type Key struct{} 37 | 38 | func withInformer(ctx context.Context) (context.Context, controller.Informer) { 39 | f := factory.Get(ctx) 40 | inf := f.Flow().V1alpha1().Transformations() 41 | return context.WithValue(ctx, Key{}, inf), inf.Informer() 42 | } 43 | 44 | // Get extracts the typed informer from the context. 45 | func Get(ctx context.Context) v1alpha1.TransformationInformer { 46 | untyped := ctx.Value(Key{}) 47 | if untyped == nil { 48 | logging.FromContext(ctx).Panic( 49 | "Unable to fetch github.com/triggermesh/bumblebee/pkg/client/generated/informers/externalversions/transformation/v1alpha1.TransformationInformer from context.") 50 | } 51 | return untyped.(v1alpha1.TransformationInformer) 52 | } 53 | -------------------------------------------------------------------------------- /pkg/client/generated/injection/reconciler/transformation/v1alpha1/transformation/controller.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 TriggerMesh Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by injection-gen. DO NOT EDIT. 18 | 19 | package transformation 20 | 21 | import ( 22 | context "context" 23 | fmt "fmt" 24 | reflect "reflect" 25 | strings "strings" 26 | 27 | internalclientsetscheme "github.com/triggermesh/bumblebee/pkg/client/generated/clientset/internalclientset/scheme" 28 | client "github.com/triggermesh/bumblebee/pkg/client/generated/injection/client" 29 | transformation "github.com/triggermesh/bumblebee/pkg/client/generated/injection/informers/transformation/v1alpha1/transformation" 30 | zap "go.uber.org/zap" 31 | corev1 "k8s.io/api/core/v1" 32 | labels "k8s.io/apimachinery/pkg/labels" 33 | types "k8s.io/apimachinery/pkg/types" 34 | watch "k8s.io/apimachinery/pkg/watch" 35 | scheme "k8s.io/client-go/kubernetes/scheme" 36 | v1 "k8s.io/client-go/kubernetes/typed/core/v1" 37 | record "k8s.io/client-go/tools/record" 38 | kubeclient "knative.dev/pkg/client/injection/kube/client" 39 | controller "knative.dev/pkg/controller" 40 | logging "knative.dev/pkg/logging" 41 | logkey "knative.dev/pkg/logging/logkey" 42 | reconciler "knative.dev/pkg/reconciler" 43 | ) 44 | 45 | const ( 46 | defaultControllerAgentName = "transformation-controller" 47 | defaultFinalizerName = "transformations.flow.triggermesh.io" 48 | ) 49 | 50 | // NewImpl returns a controller.Impl that handles queuing and feeding work from 51 | // the queue through an implementation of controller.Reconciler, delegating to 52 | // the provided Interface and optional Finalizer methods. OptionsFn is used to return 53 | // controller.Options to be used by the internal reconciler. 54 | func NewImpl(ctx context.Context, r Interface, optionsFns ...controller.OptionsFn) *controller.Impl { 55 | logger := logging.FromContext(ctx) 56 | 57 | // Check the options function input. It should be 0 or 1. 58 | if len(optionsFns) > 1 { 59 | logger.Fatal("Up to one options function is supported, found: ", len(optionsFns)) 60 | } 61 | 62 | transformationInformer := transformation.Get(ctx) 63 | 64 | lister := transformationInformer.Lister() 65 | 66 | rec := &reconcilerImpl{ 67 | LeaderAwareFuncs: reconciler.LeaderAwareFuncs{ 68 | PromoteFunc: func(bkt reconciler.Bucket, enq func(reconciler.Bucket, types.NamespacedName)) error { 69 | all, err := lister.List(labels.Everything()) 70 | if err != nil { 71 | return err 72 | } 73 | for _, elt := range all { 74 | // TODO: Consider letting users specify a filter in options. 75 | enq(bkt, types.NamespacedName{ 76 | Namespace: elt.GetNamespace(), 77 | Name: elt.GetName(), 78 | }) 79 | } 80 | return nil 81 | }, 82 | }, 83 | Client: client.Get(ctx), 84 | Lister: lister, 85 | reconciler: r, 86 | finalizerName: defaultFinalizerName, 87 | } 88 | 89 | ctrType := reflect.TypeOf(r).Elem() 90 | ctrTypeName := fmt.Sprintf("%s.%s", ctrType.PkgPath(), ctrType.Name()) 91 | ctrTypeName = strings.ReplaceAll(ctrTypeName, "/", ".") 92 | 93 | logger = logger.With( 94 | zap.String(logkey.ControllerType, ctrTypeName), 95 | zap.String(logkey.Kind, "flow.triggermesh.io.Transformation"), 96 | ) 97 | 98 | impl := controller.NewImpl(rec, logger, ctrTypeName) 99 | agentName := defaultControllerAgentName 100 | 101 | // Pass impl to the options. Save any optional results. 102 | for _, fn := range optionsFns { 103 | opts := fn(impl) 104 | if opts.ConfigStore != nil { 105 | rec.configStore = opts.ConfigStore 106 | } 107 | if opts.FinalizerName != "" { 108 | rec.finalizerName = opts.FinalizerName 109 | } 110 | if opts.AgentName != "" { 111 | agentName = opts.AgentName 112 | } 113 | if opts.SkipStatusUpdates { 114 | rec.skipStatusUpdates = true 115 | } 116 | if opts.DemoteFunc != nil { 117 | rec.DemoteFunc = opts.DemoteFunc 118 | } 119 | } 120 | 121 | rec.Recorder = createRecorder(ctx, agentName) 122 | 123 | return impl 124 | } 125 | 126 | func createRecorder(ctx context.Context, agentName string) record.EventRecorder { 127 | logger := logging.FromContext(ctx) 128 | 129 | recorder := controller.GetEventRecorder(ctx) 130 | if recorder == nil { 131 | // Create event broadcaster 132 | logger.Debug("Creating event broadcaster") 133 | eventBroadcaster := record.NewBroadcaster() 134 | watches := []watch.Interface{ 135 | eventBroadcaster.StartLogging(logger.Named("event-broadcaster").Infof), 136 | eventBroadcaster.StartRecordingToSink( 137 | &v1.EventSinkImpl{Interface: kubeclient.Get(ctx).CoreV1().Events("")}), 138 | } 139 | recorder = eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: agentName}) 140 | go func() { 141 | <-ctx.Done() 142 | for _, w := range watches { 143 | w.Stop() 144 | } 145 | }() 146 | } 147 | 148 | return recorder 149 | } 150 | 151 | func init() { 152 | internalclientsetscheme.AddToScheme(scheme.Scheme) 153 | } 154 | -------------------------------------------------------------------------------- /pkg/client/generated/injection/reconciler/transformation/v1alpha1/transformation/state.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 TriggerMesh Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by injection-gen. DO NOT EDIT. 18 | 19 | package transformation 20 | 21 | import ( 22 | fmt "fmt" 23 | 24 | v1alpha1 "github.com/triggermesh/bumblebee/pkg/apis/transformation/v1alpha1" 25 | types "k8s.io/apimachinery/pkg/types" 26 | cache "k8s.io/client-go/tools/cache" 27 | reconciler "knative.dev/pkg/reconciler" 28 | ) 29 | 30 | // state is used to track the state of a reconciler in a single run. 31 | type state struct { 32 | // key is the original reconciliation key from the queue. 33 | key string 34 | // namespace is the namespace split from the reconciliation key. 35 | namespace string 36 | // name is the name split from the reconciliation key. 37 | name string 38 | // reconciler is the reconciler. 39 | reconciler Interface 40 | // roi is the read only interface cast of the reconciler. 41 | roi ReadOnlyInterface 42 | // isROI (Read Only Interface) the reconciler only observes reconciliation. 43 | isROI bool 44 | // rof is the read only finalizer cast of the reconciler. 45 | rof ReadOnlyFinalizer 46 | // isROF (Read Only Finalizer) the reconciler only observes finalize. 47 | isROF bool 48 | // isLeader the instance of the reconciler is the elected leader. 49 | isLeader bool 50 | } 51 | 52 | func newState(key string, r *reconcilerImpl) (*state, error) { 53 | // Convert the namespace/name string into a distinct namespace and name. 54 | namespace, name, err := cache.SplitMetaNamespaceKey(key) 55 | if err != nil { 56 | return nil, fmt.Errorf("invalid resource key: %s", key) 57 | } 58 | 59 | roi, isROI := r.reconciler.(ReadOnlyInterface) 60 | rof, isROF := r.reconciler.(ReadOnlyFinalizer) 61 | 62 | isLeader := r.IsLeaderFor(types.NamespacedName{ 63 | Namespace: namespace, 64 | Name: name, 65 | }) 66 | 67 | return &state{ 68 | key: key, 69 | namespace: namespace, 70 | name: name, 71 | reconciler: r.reconciler, 72 | roi: roi, 73 | isROI: isROI, 74 | rof: rof, 75 | isROF: isROF, 76 | isLeader: isLeader, 77 | }, nil 78 | } 79 | 80 | // isNotLeaderNorObserver checks to see if this reconciler with the current 81 | // state is enabled to do any work or not. 82 | // isNotLeaderNorObserver returns true when there is no work possible for the 83 | // reconciler. 84 | func (s *state) isNotLeaderNorObserver() bool { 85 | if !s.isLeader && !s.isROI && !s.isROF { 86 | // If we are not the leader, and we don't implement either ReadOnly 87 | // interface, then take a fast-path out. 88 | return true 89 | } 90 | return false 91 | } 92 | 93 | func (s *state) reconcileMethodFor(o *v1alpha1.Transformation) (string, doReconcile) { 94 | if o.GetDeletionTimestamp().IsZero() { 95 | if s.isLeader { 96 | return reconciler.DoReconcileKind, s.reconciler.ReconcileKind 97 | } else if s.isROI { 98 | return reconciler.DoObserveKind, s.roi.ObserveKind 99 | } 100 | } else if fin, ok := s.reconciler.(Finalizer); s.isLeader && ok { 101 | return reconciler.DoFinalizeKind, fin.FinalizeKind 102 | } else if !s.isLeader && s.isROF { 103 | return reconciler.DoObserveFinalizeKind, s.rof.ObserveFinalizeKind 104 | } 105 | return "unknown", nil 106 | } 107 | -------------------------------------------------------------------------------- /pkg/client/generated/listers/transformation/v1alpha1/expansion_generated.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 TriggerMesh Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by lister-gen. DO NOT EDIT. 18 | 19 | package v1alpha1 20 | 21 | // TransformationListerExpansion allows custom methods to be added to 22 | // TransformationLister. 23 | type TransformationListerExpansion interface{} 24 | 25 | // TransformationNamespaceListerExpansion allows custom methods to be added to 26 | // TransformationNamespaceLister. 27 | type TransformationNamespaceListerExpansion interface{} 28 | -------------------------------------------------------------------------------- /pkg/client/generated/listers/transformation/v1alpha1/transformation.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 TriggerMesh Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by lister-gen. DO NOT EDIT. 18 | 19 | package v1alpha1 20 | 21 | import ( 22 | v1alpha1 "github.com/triggermesh/bumblebee/pkg/apis/transformation/v1alpha1" 23 | "k8s.io/apimachinery/pkg/api/errors" 24 | "k8s.io/apimachinery/pkg/labels" 25 | "k8s.io/client-go/tools/cache" 26 | ) 27 | 28 | // TransformationLister helps list Transformations. 29 | // All objects returned here must be treated as read-only. 30 | type TransformationLister interface { 31 | // List lists all Transformations in the indexer. 32 | // Objects returned here must be treated as read-only. 33 | List(selector labels.Selector) (ret []*v1alpha1.Transformation, err error) 34 | // Transformations returns an object that can list and get Transformations. 35 | Transformations(namespace string) TransformationNamespaceLister 36 | TransformationListerExpansion 37 | } 38 | 39 | // transformationLister implements the TransformationLister interface. 40 | type transformationLister struct { 41 | indexer cache.Indexer 42 | } 43 | 44 | // NewTransformationLister returns a new TransformationLister. 45 | func NewTransformationLister(indexer cache.Indexer) TransformationLister { 46 | return &transformationLister{indexer: indexer} 47 | } 48 | 49 | // List lists all Transformations in the indexer. 50 | func (s *transformationLister) List(selector labels.Selector) (ret []*v1alpha1.Transformation, err error) { 51 | err = cache.ListAll(s.indexer, selector, func(m interface{}) { 52 | ret = append(ret, m.(*v1alpha1.Transformation)) 53 | }) 54 | return ret, err 55 | } 56 | 57 | // Transformations returns an object that can list and get Transformations. 58 | func (s *transformationLister) Transformations(namespace string) TransformationNamespaceLister { 59 | return transformationNamespaceLister{indexer: s.indexer, namespace: namespace} 60 | } 61 | 62 | // TransformationNamespaceLister helps list and get Transformations. 63 | // All objects returned here must be treated as read-only. 64 | type TransformationNamespaceLister interface { 65 | // List lists all Transformations in the indexer for a given namespace. 66 | // Objects returned here must be treated as read-only. 67 | List(selector labels.Selector) (ret []*v1alpha1.Transformation, err error) 68 | // Get retrieves the Transformation from the indexer for a given namespace and name. 69 | // Objects returned here must be treated as read-only. 70 | Get(name string) (*v1alpha1.Transformation, error) 71 | TransformationNamespaceListerExpansion 72 | } 73 | 74 | // transformationNamespaceLister implements the TransformationNamespaceLister 75 | // interface. 76 | type transformationNamespaceLister struct { 77 | indexer cache.Indexer 78 | namespace string 79 | } 80 | 81 | // List lists all Transformations in the indexer for a given namespace. 82 | func (s transformationNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.Transformation, err error) { 83 | err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { 84 | ret = append(ret, m.(*v1alpha1.Transformation)) 85 | }) 86 | return ret, err 87 | } 88 | 89 | // Get retrieves the Transformation from the indexer for a given namespace and name. 90 | func (s transformationNamespaceLister) Get(name string) (*v1alpha1.Transformation, error) { 91 | obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) 92 | if err != nil { 93 | return nil, err 94 | } 95 | if !exists { 96 | return nil, errors.NewNotFound(v1alpha1.Resource("transformation"), name) 97 | } 98 | return obj.(*v1alpha1.Transformation), nil 99 | } 100 | -------------------------------------------------------------------------------- /pkg/pipeline/common/convert/convert.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 TriggerMesh Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package convert 18 | 19 | import ( 20 | "strconv" 21 | "strings" 22 | ) 23 | 24 | // SliceToMap converts string slice into map that can be encoded into JSON. 25 | func SliceToMap(path []string, value interface{}) map[string]interface{} { 26 | var array bool 27 | var index int 28 | i := strings.Index(path[0], "[") 29 | if i > -1 && len(path[0]) > i+1 { 30 | indexStr := path[0][i+1 : len(path[0])-1] 31 | indexInt, err := strconv.Atoi(indexStr) 32 | if err == nil { 33 | index = indexInt 34 | array = true 35 | path[0] = path[0][:i] 36 | } 37 | } 38 | 39 | if len(path) == 1 { 40 | if !array { 41 | return map[string]interface{}{ 42 | path[0]: value, 43 | } 44 | } 45 | arr := make([]interface{}, index+1) 46 | arr[index] = value 47 | return map[string]interface{}{ 48 | path[0]: arr, 49 | } 50 | } 51 | 52 | key := path[0] 53 | path = path[1:] 54 | m := SliceToMap(path, value) 55 | if !array { 56 | return map[string]interface{}{ 57 | key: m, 58 | } 59 | } 60 | arr := make([]interface{}, index+1) 61 | arr[index] = m 62 | return map[string]interface{}{ 63 | key: arr, 64 | } 65 | } 66 | 67 | // MergeJSONWithMap accepts interface (effectively, JSON) and a map and merges them together. 68 | // Source map keys are being overwritten by appendix keys if they overlap. 69 | func MergeJSONWithMap(source, appendix interface{}) interface{} { 70 | switch appendixValue := appendix.(type) { 71 | case float64, bool, string, nil: 72 | return appendixValue 73 | case []interface{}: 74 | sourceInterface, ok := source.([]interface{}) 75 | if !ok { 76 | return appendixValue 77 | } 78 | resArrLen := len(sourceInterface) 79 | if len(appendixValue) > resArrLen { 80 | resArrLen = len(appendixValue) 81 | } 82 | resArr := make([]interface{}, resArrLen) 83 | for i := range resArr { 84 | if i < len(appendixValue) && appendixValue[i] != nil { 85 | resArr[i] = appendixValue[i] 86 | continue 87 | } 88 | if i < len(sourceInterface) { 89 | resArr[i] = sourceInterface[i] 90 | } 91 | } 92 | source = resArr 93 | case map[string]interface{}: 94 | switch s := source.(type) { 95 | case nil: 96 | source = make(map[string]interface{}) 97 | return MergeJSONWithMap(source, appendixValue) 98 | case map[string]interface{}: 99 | for k, v := range appendixValue { 100 | if k == "" { 101 | return MergeJSONWithMap(s, v) 102 | } 103 | s[k] = MergeJSONWithMap(s[k], v) 104 | } 105 | source = s 106 | case []interface{}: 107 | for k, v := range appendixValue { 108 | if k == "" { 109 | return MergeJSONWithMap(s, v) 110 | } 111 | } 112 | } 113 | } 114 | return source 115 | } 116 | -------------------------------------------------------------------------------- /pkg/pipeline/common/convert/convert_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 TriggerMesh Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package convert 18 | 19 | import ( 20 | "encoding/json" 21 | "strings" 22 | "testing" 23 | 24 | "github.com/stretchr/testify/assert" 25 | ) 26 | 27 | func TestSliceToMap(t *testing.T) { 28 | testCases := []struct { 29 | path string 30 | value string 31 | result map[string]interface{} 32 | }{ 33 | { 34 | path: "foo.bar", 35 | value: "", 36 | result: map[string]interface{}{ 37 | "foo": map[string]interface{}{ 38 | "bar": "", 39 | }, 40 | }, 41 | }, 42 | { 43 | path: "foo.[0].bar", 44 | value: "", 45 | result: map[string]interface{}{ 46 | "foo": map[string]interface{}{ 47 | "": []interface{}{ 48 | map[string]interface{}{ 49 | "bar": "", 50 | }, 51 | }, 52 | }, 53 | }, 54 | }, 55 | { 56 | path: "[1].foo", 57 | value: "bar", 58 | result: map[string]interface{}{ 59 | "": []interface{}{ 60 | nil, 61 | map[string]interface{}{ 62 | "foo": "bar", 63 | }, 64 | }, 65 | }, 66 | }, 67 | } 68 | 69 | for _, tc := range testCases { 70 | t.Run(tc.path, func(t *testing.T) { 71 | assert.Equal(t, tc.result, SliceToMap(strings.Split(tc.path, "."), tc.value)) 72 | }) 73 | } 74 | } 75 | 76 | func TestMergeJSONWithMap(t *testing.T) { 77 | testCases := []struct { 78 | source string 79 | appendix string 80 | result interface{} 81 | }{ 82 | { 83 | source: `{"old":"value"}`, 84 | appendix: "foo.bar", 85 | result: map[string]interface{}{ 86 | "old": "value", 87 | "foo": "bar", 88 | }, 89 | }, { 90 | source: `{"old":"value"}`, 91 | appendix: "foo.bar[1].baz", 92 | result: map[string]interface{}{ 93 | "old": "value", 94 | "foo": map[string]interface{}{ 95 | "bar": []interface{}{ 96 | nil, 97 | "baz", 98 | }, 99 | }, 100 | }, 101 | }, { 102 | source: `{"old":"value"}`, 103 | appendix: "[1].foo.bar", 104 | result: []interface{}{ 105 | nil, 106 | map[string]interface{}{ 107 | "foo": "bar", 108 | }, 109 | }, 110 | }, 111 | } 112 | 113 | for _, tc := range testCases { 114 | t.Run(tc.appendix, func(t *testing.T) { 115 | var data interface{} 116 | assert.NoError(t, json.Unmarshal([]byte(tc.source), &data)) 117 | s := strings.Split(tc.appendix, ".") 118 | appendix := SliceToMap(s[:len(s)-1], s[len(s)-1]) 119 | assert.Equal(t, MergeJSONWithMap(data, appendix), tc.result) 120 | }) 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /pkg/pipeline/common/storage/storage.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 TriggerMesh Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package storage 18 | 19 | import "sync" 20 | 21 | // Storage is a simple object that provides thread safe 22 | // methods to read and write into a map. 23 | type Storage struct { 24 | data map[string]interface{} 25 | mux sync.RWMutex 26 | } 27 | 28 | // New returns an instance of Storage. 29 | func New() *Storage { 30 | return &Storage{ 31 | data: make(map[string]interface{}), 32 | mux: sync.RWMutex{}, 33 | } 34 | } 35 | 36 | // Set writes a value interface to a string key. 37 | func (s *Storage) Set(k string, v interface{}) { 38 | s.mux.Lock() 39 | s.data[k] = v 40 | s.mux.Unlock() 41 | } 42 | 43 | // Get reads value by a key. 44 | func (s *Storage) Get(k string) interface{} { 45 | s.mux.RLock() 46 | defer s.mux.RUnlock() 47 | return s.data[k] 48 | } 49 | 50 | // ListKeys returns the slice of var keys stored in memory. 51 | func (s *Storage) ListKeys() []string { 52 | s.mux.RLock() 53 | defer s.mux.RUnlock() 54 | list := []string{} 55 | for k := range s.data { 56 | list = append(list, k) 57 | } 58 | return list 59 | } 60 | -------------------------------------------------------------------------------- /pkg/pipeline/handler.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 TriggerMesh Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package pipeline 18 | 19 | import ( 20 | "context" 21 | "encoding/json" 22 | "fmt" 23 | "log" 24 | "strings" 25 | 26 | cloudevents "github.com/cloudevents/sdk-go/v2" 27 | 28 | "github.com/triggermesh/bumblebee/pkg/apis/transformation/v1alpha1" 29 | "github.com/triggermesh/bumblebee/pkg/pipeline/common/storage" 30 | ) 31 | 32 | // Handler contains Pipelines for CE transformations and CloudEvents client. 33 | type Handler struct { 34 | ContextPipeline *Pipeline 35 | DataPipeline *Pipeline 36 | 37 | client cloudevents.Client 38 | } 39 | 40 | // ceContext represents CloudEvents context structure but with exported Extensions. 41 | type ceContext struct { 42 | *cloudevents.EventContextV1 `json:",inline"` 43 | Extensions map[string]interface{} `json:"Extensions,omitempty"` 44 | } 45 | 46 | // NewHandler creates Handler instance. 47 | func NewHandler(context, data []v1alpha1.Transform) (Handler, error) { 48 | contextPipeline, err := newPipeline(context) 49 | if err != nil { 50 | return Handler{}, err 51 | } 52 | 53 | dataPipeline, err := newPipeline(data) 54 | if err != nil { 55 | return Handler{}, err 56 | } 57 | 58 | sharedVars := storage.New() 59 | contextPipeline.setStorage(sharedVars) 60 | dataPipeline.setStorage(sharedVars) 61 | 62 | ceClient, err := cloudevents.NewDefaultClient() 63 | if err != nil { 64 | return Handler{}, err 65 | } 66 | 67 | return Handler{ 68 | ContextPipeline: contextPipeline, 69 | DataPipeline: dataPipeline, 70 | 71 | client: ceClient, 72 | }, nil 73 | } 74 | 75 | // Start runs CloudEvent receiver and applies transformation Pipeline 76 | // on incoming events. 77 | func (t *Handler) Start(ctx context.Context, sink string) error { 78 | log.Println("Starting CloudEvent receiver") 79 | var receiver interface{} 80 | receiver = t.receiveAndReply 81 | if sink != "" { 82 | ctx = cloudevents.ContextWithTarget(ctx, sink) 83 | receiver = t.receiveAndSend 84 | } 85 | 86 | return t.client.StartReceiver(ctx, receiver) 87 | } 88 | 89 | func (t *Handler) receiveAndReply(event cloudevents.Event) (*cloudevents.Event, error) { 90 | return t.applyTransformations(event) 91 | } 92 | 93 | func (t *Handler) receiveAndSend(ctx context.Context, event cloudevents.Event) error { 94 | result, err := t.applyTransformations(event) 95 | if err != nil { 96 | return err 97 | } 98 | return t.client.Send(ctx, *result) 99 | } 100 | 101 | func (t *Handler) applyTransformations(event cloudevents.Event) (*cloudevents.Event, error) { 102 | log.Printf("Received %q event", event.Type()) 103 | // HTTPTargets sets content type from HTTP headers, i.e.: 104 | // "datacontenttype: application/json; charset=utf-8" 105 | // so we must use "contains" instead of strict equality 106 | if !strings.Contains(event.DataContentType(), cloudevents.ApplicationJSON) { 107 | log.Printf("CE Content Type %q is not supported", event.DataContentType()) 108 | return nil, fmt.Errorf("CE Content Type %q is not supported", event.DataContentType()) 109 | } 110 | 111 | localContext := ceContext{ 112 | EventContextV1: event.Context.AsV1(), 113 | Extensions: event.Context.AsV1().GetExtensions(), 114 | } 115 | 116 | localContextBytes, err := json.Marshal(localContext) 117 | if err != nil { 118 | log.Printf("Cannot encode CE context: %v", err) 119 | return nil, fmt.Errorf("cannot encode CE context: %w", err) 120 | } 121 | 122 | // Run init step such as load Pipeline variables first 123 | t.ContextPipeline.initStep(localContextBytes) 124 | t.DataPipeline.initStep(event.Data()) 125 | 126 | // CE Context transformation 127 | localContextBytes, err = t.ContextPipeline.apply(localContextBytes) 128 | if err != nil { 129 | log.Printf("Cannot apply transformation on CE context: %v", err) 130 | return nil, fmt.Errorf("cannot apply transformation on CE context: %w", err) 131 | } 132 | 133 | if err := json.Unmarshal(localContextBytes, &localContext); err != nil { 134 | log.Printf("Cannot decode CE new context: %v", err) 135 | return nil, fmt.Errorf("cannot decode CE new context: %w", err) 136 | } 137 | event.Context = localContext 138 | for k, v := range localContext.Extensions { 139 | if err := event.Context.SetExtension(k, v); err != nil { 140 | log.Printf("Cannot set CE extension: %v", err) 141 | return nil, fmt.Errorf("cannot set CE extension: %w", err) 142 | } 143 | } 144 | 145 | // CE Data transformation 146 | data, err := t.DataPipeline.apply(event.Data()) 147 | if err != nil { 148 | log.Printf("Cannot apply transformation on CE data: %v", err) 149 | return nil, fmt.Errorf("cannot apply transformation on CE data: %w", err) 150 | } 151 | if err = event.SetData(cloudevents.ApplicationJSON, data); err != nil { 152 | log.Printf("Cannot set data: %v", err) 153 | return nil, fmt.Errorf("cannot set data: %w", err) 154 | } 155 | 156 | log.Printf("Sending %q event", event.Type()) 157 | return &event, nil 158 | } 159 | -------------------------------------------------------------------------------- /pkg/pipeline/handler_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 TriggerMesh Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package pipeline 18 | 19 | import ( 20 | "context" 21 | "encoding/json" 22 | "testing" 23 | "time" 24 | 25 | cloudevents "github.com/cloudevents/sdk-go/v2" 26 | "github.com/stretchr/testify/assert" 27 | 28 | "github.com/triggermesh/bumblebee/pkg/apis/transformation/v1alpha1" 29 | ) 30 | 31 | var availableTransformations = []v1alpha1.Transform{ 32 | {Operation: "add"}, 33 | {Operation: "store"}, 34 | {Operation: "shift"}, 35 | {Operation: "delete"}, 36 | } 37 | 38 | func TestNewHandler(t *testing.T) { 39 | _, err := NewHandler(availableTransformations, availableTransformations) 40 | assert.NoError(t, err) 41 | } 42 | 43 | func TestStart(t *testing.T) { 44 | ctx, cancel := context.WithCancel(context.Background()) 45 | 46 | pipeline, err := NewHandler(availableTransformations, availableTransformations) 47 | assert.NoError(t, err) 48 | 49 | errChan := make(chan error) 50 | 51 | go func() { 52 | defer close(errChan) 53 | errChan <- pipeline.Start(ctx, "") 54 | }() 55 | 56 | cancel() 57 | 58 | waitCtx, waitCancel := context.WithTimeout(context.Background(), 3*time.Second) 59 | defer waitCancel() 60 | 61 | select { 62 | case err := <-errChan: 63 | assert.NoError(t, err) 64 | case <-waitCtx.Done(): 65 | t.Error("Start() shutdown by context wait timeout") 66 | } 67 | } 68 | 69 | func setData(t *testing.T, event cloudevents.Event, data interface{}) cloudevents.Event { 70 | assert.NoError(t, event.SetData(cloudevents.ApplicationJSON, data)) 71 | return event 72 | } 73 | 74 | func newEvent() cloudevents.Event { 75 | emptyV1Event := cloudevents.NewEvent(cloudevents.VersionV1) 76 | emptyV1Event.SetID("123") 77 | emptyV1Event.SetSource("test") 78 | emptyV1Event.SetType("test") 79 | return emptyV1Event 80 | } 81 | 82 | func TestReceiveAndTransform(t *testing.T) { 83 | testCases := []struct { 84 | name string 85 | originalEvent cloudevents.Event 86 | expectedEventData string 87 | data []v1alpha1.Transform 88 | }{ 89 | { 90 | name: "Add operation", 91 | originalEvent: setData(t, newEvent(), 92 | json.RawMessage(`{"foo":"bar","blah":[{"bleh":"huh?"}]}`)), 93 | expectedEventData: `{"blah":[{"bleh":"no"},null,{"foo":"42"}],"foo":"baz","message":"Hello World!","object":{"message":"hey","slice":[null,"sup"]}}`, 94 | data: []v1alpha1.Transform{ 95 | { 96 | Operation: "add", 97 | Paths: []v1alpha1.Path{ 98 | { 99 | // add key 100 | Key: "message", 101 | Value: "Hello World!", 102 | }, { 103 | // add object 104 | Key: "object.message", 105 | Value: "hey", 106 | }, { 107 | // append array value 108 | Key: "object.slice[1]", 109 | Value: "sup", 110 | }, { 111 | // append object to an array 112 | Key: "blah[2].foo", 113 | Value: "42", 114 | }, { 115 | // overwrite object in the array 116 | Key: "blah[0].bleh", 117 | Value: "no", 118 | }, { 119 | // overwrite original key 120 | Key: "foo", 121 | Value: "baz", 122 | }, 123 | }, 124 | }, 125 | }, 126 | }, { 127 | name: "Delete operation", 128 | originalEvent: setData(t, newEvent(), 129 | json.RawMessage(`{"key1":"value1","key2":"value2","key3":"value3","key4":"value4"}`)), 130 | expectedEventData: `{"key2":"value2"}`, 131 | data: []v1alpha1.Transform{ 132 | { 133 | Operation: "delete", 134 | Paths: []v1alpha1.Path{ 135 | { 136 | // just delete the key 137 | Key: "key1", 138 | Value: "", 139 | }, { 140 | // actual value is not equal to the filter, ignore 141 | Key: "key2", 142 | Value: "wrong filter", 143 | }, { 144 | // actual and expected values are equal, delete 145 | Key: "key3", 146 | Value: "value3", 147 | }, { 148 | // delete all keys with this value 149 | Value: "value4", 150 | }, 151 | }, 152 | }, 153 | }, 154 | }, { 155 | name: "Delete operation, wipe payload", 156 | originalEvent: setData(t, newEvent(), 157 | json.RawMessage(`{"key1":"value1"}`)), 158 | expectedEventData: "null", 159 | data: []v1alpha1.Transform{ 160 | { 161 | // wipe event payload 162 | Operation: "delete", 163 | Paths: []v1alpha1.Path{ 164 | { 165 | Key: "", 166 | }, 167 | }, 168 | }, 169 | }, 170 | }, { 171 | name: "Shift operation", 172 | originalEvent: setData(t, newEvent(), 173 | json.RawMessage(`{"key1":"value1","object":{"key2":"value2"}}`)), 174 | expectedEventData: `{"key1":"value1","object":{},"okey":"value2"}`, 175 | data: []v1alpha1.Transform{ 176 | { 177 | Operation: "shift", 178 | Paths: []v1alpha1.Path{ 179 | { 180 | Key: "object.key2:okey", 181 | }, { 182 | Key: "key1:key2", 183 | Value: "wrong filter", 184 | }, 185 | }, 186 | }, 187 | }, 188 | }, { 189 | name: "Store operation", 190 | originalEvent: setData(t, newEvent(), 191 | json.RawMessage(`{"key1":"value1","object":{"foo":"bar"}}`)), 192 | expectedEventData: `{"key1":"bar","object":{"foo":"value1"}}`, 193 | data: []v1alpha1.Transform{ 194 | { 195 | Operation: "store", 196 | Paths: []v1alpha1.Path{ 197 | { 198 | Key: "$var1", 199 | Value: "key1", 200 | }, { 201 | Key: "$var2", 202 | Value: "object.foo", 203 | }, 204 | }, 205 | }, { 206 | Operation: "add", 207 | Paths: []v1alpha1.Path{ 208 | { 209 | Key: "key1", 210 | Value: "$var2", 211 | }, { 212 | Key: "object.foo", 213 | Value: "$var1", 214 | }, 215 | }, 216 | }, 217 | }, 218 | }, 219 | } 220 | 221 | for _, tc := range testCases { 222 | t.Run(tc.name, func(t *testing.T) { 223 | pipeline, err := NewHandler([]v1alpha1.Transform{}, tc.data) 224 | assert.NoError(t, err) 225 | 226 | transformedEvent, err := pipeline.applyTransformations(tc.originalEvent) 227 | assert.NoError(t, err) 228 | 229 | assert.Equal(t, tc.expectedEventData, string(transformedEvent.Data())) 230 | }) 231 | } 232 | } 233 | -------------------------------------------------------------------------------- /pkg/pipeline/pipeline.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 TriggerMesh Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package pipeline 18 | 19 | import ( 20 | "fmt" 21 | "log" 22 | 23 | "github.com/triggermesh/bumblebee/pkg/apis/transformation/v1alpha1" 24 | "github.com/triggermesh/bumblebee/pkg/pipeline/common/storage" 25 | "github.com/triggermesh/bumblebee/pkg/pipeline/transformer" 26 | "github.com/triggermesh/bumblebee/pkg/pipeline/transformer/add" 27 | "github.com/triggermesh/bumblebee/pkg/pipeline/transformer/delete" 28 | "github.com/triggermesh/bumblebee/pkg/pipeline/transformer/shift" 29 | "github.com/triggermesh/bumblebee/pkg/pipeline/transformer/store" 30 | ) 31 | 32 | // Pipeline is a set of Transformations that are 33 | // sequentially applied to JSON data. 34 | type Pipeline struct { 35 | Transformers []transformer.Transformer 36 | } 37 | 38 | // register loads available Transformation into a named map. 39 | func register() map[string]transformer.Transformer { 40 | transformations := make(map[string]transformer.Transformer) 41 | 42 | add.Register(transformations) 43 | delete.Register(transformations) 44 | shift.Register(transformations) 45 | store.Register(transformations) 46 | 47 | return transformations 48 | } 49 | 50 | // newPipeline loads available Transformations and creates a Pipeline. 51 | func newPipeline(transformations []v1alpha1.Transform) (*Pipeline, error) { 52 | availableTransformers := register() 53 | pipeline := []transformer.Transformer{} 54 | 55 | for _, transformation := range transformations { 56 | operation, exist := availableTransformers[transformation.Operation] 57 | if !exist { 58 | return nil, fmt.Errorf("transformation %q not found", transformation.Operation) 59 | } 60 | for _, kv := range transformation.Paths { 61 | pipeline = append(pipeline, operation.New(kv.Key, kv.Value)) 62 | log.Printf("%s: %s", transformation.Operation, kv.Key) 63 | } 64 | } 65 | 66 | return &Pipeline{ 67 | Transformers: pipeline, 68 | }, nil 69 | } 70 | 71 | // SetStorage injects shared storage with Pipeline vars. 72 | func (p *Pipeline) setStorage(s *storage.Storage) { 73 | for _, v := range p.Transformers { 74 | v.SetStorage(s) 75 | } 76 | } 77 | 78 | // InitStep runs Transformations that are marked as InitStep. 79 | func (p *Pipeline) initStep(data []byte) { 80 | for _, v := range p.Transformers { 81 | if !v.InitStep() { 82 | continue 83 | } 84 | if _, err := v.Apply(data); err != nil { 85 | log.Printf("Failed to apply Init step: %v", err) 86 | } 87 | } 88 | } 89 | 90 | // Apply applies Pipeline transformations. 91 | func (p *Pipeline) apply(data []byte) ([]byte, error) { 92 | var err error 93 | for _, v := range p.Transformers { 94 | if v.InitStep() { 95 | continue 96 | } 97 | data, err = v.Apply(data) 98 | if err != nil { 99 | return data, err 100 | } 101 | } 102 | return data, nil 103 | } 104 | -------------------------------------------------------------------------------- /pkg/pipeline/transformer/add/add.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 TriggerMesh Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package add 18 | 19 | import ( 20 | "encoding/json" 21 | "fmt" 22 | "strings" 23 | 24 | "github.com/triggermesh/bumblebee/pkg/pipeline/common/convert" 25 | "github.com/triggermesh/bumblebee/pkg/pipeline/common/storage" 26 | "github.com/triggermesh/bumblebee/pkg/pipeline/transformer" 27 | ) 28 | 29 | var _ transformer.Transformer = (*Add)(nil) 30 | 31 | // Add object implements Transformer interface. 32 | type Add struct { 33 | Path string 34 | Value string 35 | 36 | variables *storage.Storage 37 | } 38 | 39 | // InitStep is used to figure out if this operation should 40 | // run before main Transformations. For example, Store 41 | // operation needs to run first to load all Pipeline variables. 42 | var InitStep bool = false 43 | 44 | // operationName is used to identify this transformation. 45 | var operationName string = "add" 46 | 47 | // Register adds this transformation to the map which will 48 | // be used to create Transformation pipeline. 49 | func Register(m map[string]transformer.Transformer) { 50 | m[operationName] = &Add{} 51 | } 52 | 53 | // SetStorage sets a shared Storage with Pipeline variables. 54 | func (a *Add) SetStorage(storage *storage.Storage) { 55 | a.variables = storage 56 | } 57 | 58 | // InitStep returns "true" if this Transformation should run 59 | // as init step. 60 | func (a *Add) InitStep() bool { 61 | return InitStep 62 | } 63 | 64 | // New returns a new instance of Add object. 65 | func (a *Add) New(key, value string) transformer.Transformer { 66 | return &Add{ 67 | Path: key, 68 | Value: value, 69 | 70 | variables: a.variables, 71 | } 72 | } 73 | 74 | // Apply is a main method of Transformation that adds any type of 75 | // variables into existing JSON. 76 | func (a *Add) Apply(data []byte) ([]byte, error) { 77 | input := convert.SliceToMap(strings.Split(a.Path, "."), a.composeValue()) 78 | var event interface{} 79 | if err := json.Unmarshal(data, &event); err != nil { 80 | return data, err 81 | } 82 | 83 | result := convert.MergeJSONWithMap(event, input) 84 | output, err := json.Marshal(result) 85 | if err != nil { 86 | return data, err 87 | } 88 | 89 | return output, nil 90 | } 91 | 92 | func (a *Add) retrieveVariable(key string) interface{} { 93 | if value := a.variables.Get(key); value != nil { 94 | return value 95 | } 96 | return key 97 | } 98 | 99 | func (a *Add) composeValue() interface{} { 100 | result := a.Value 101 | for _, key := range a.variables.ListKeys() { 102 | index := strings.Index(result, key) 103 | if index == -1 { 104 | continue 105 | } 106 | if result == key { 107 | return a.retrieveVariable(key) 108 | } 109 | result = fmt.Sprintf("%s%v%s", result[:index], a.retrieveVariable(key), result[index+len(key):]) 110 | } 111 | return result 112 | } 113 | -------------------------------------------------------------------------------- /pkg/pipeline/transformer/delete/delete.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 TriggerMesh Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package delete 18 | 19 | import ( 20 | "encoding/json" 21 | "fmt" 22 | "log" 23 | "strconv" 24 | 25 | "github.com/triggermesh/bumblebee/pkg/pipeline/common/storage" 26 | "github.com/triggermesh/bumblebee/pkg/pipeline/transformer" 27 | ) 28 | 29 | var _ transformer.Transformer = (*Delete)(nil) 30 | 31 | // Delete object implements Transformer interface. 32 | type Delete struct { 33 | Path string 34 | Value string 35 | Type string 36 | 37 | variables *storage.Storage 38 | } 39 | 40 | // InitStep is used to figure out if this operation should 41 | // run before main Transformations. For example, Store 42 | // operation needs to run first to load all Pipeline variables. 43 | var InitStep bool = false 44 | 45 | // operationName is used to identify this transformation. 46 | var operationName string = "delete" 47 | 48 | // Register adds this transformation to the map which will 49 | // be used to create Transformation pipeline. 50 | func Register(m map[string]transformer.Transformer) { 51 | m[operationName] = &Delete{} 52 | } 53 | 54 | // SetStorage sets a shared Storage with Pipeline variables. 55 | func (d *Delete) SetStorage(storage *storage.Storage) { 56 | d.variables = storage 57 | } 58 | 59 | // InitStep returns "true" if this Transformation should run 60 | // as init step. 61 | func (d *Delete) InitStep() bool { 62 | return InitStep 63 | } 64 | 65 | // New returns a new instance of Delete object. 66 | func (d *Delete) New(key, value string) transformer.Transformer { 67 | return &Delete{ 68 | Path: key, 69 | Value: value, 70 | 71 | variables: d.variables, 72 | } 73 | } 74 | 75 | // Apply is a main method of Transformation that removed any type of 76 | // variables from existing JSON. 77 | func (d *Delete) Apply(data []byte) ([]byte, error) { 78 | d.Value = d.retrieveString(d.Value) 79 | 80 | result, err := d.parse(data, "", "") 81 | if err != nil { 82 | return data, err 83 | } 84 | 85 | output, err := json.Marshal(result) 86 | if err != nil { 87 | return data, err 88 | } 89 | 90 | return output, nil 91 | } 92 | 93 | func (d *Delete) retrieveString(key string) string { 94 | if value := d.variables.Get(key); value != nil { 95 | if str, ok := value.(string); ok { 96 | return str 97 | } 98 | } 99 | return key 100 | } 101 | 102 | func (d *Delete) parse(data interface{}, key, path string) (interface{}, error) { 103 | output := make(map[string]interface{}) 104 | // TODO: keep only one filter call 105 | if d.filter(path, data) { 106 | return nil, nil 107 | } 108 | switch value := data.(type) { 109 | case []byte: 110 | var m interface{} 111 | if err := json.Unmarshal(value, &m); err != nil { 112 | return nil, fmt.Errorf("unmarshal err: %v", err) 113 | } 114 | o, err := d.parse(m, key, path) 115 | if err != nil { 116 | return nil, fmt.Errorf("recursive call in []bytes case: %v", err) 117 | } 118 | return o, nil 119 | case float64, bool, string, nil: 120 | return value, nil 121 | case []interface{}: 122 | slice := []interface{}{} 123 | for i, v := range value { 124 | o, err := d.parse(v, key, fmt.Sprintf("%s[%d]", path, i)) 125 | if err != nil { 126 | return nil, fmt.Errorf("recursive call in []interface case: %v", err) 127 | } 128 | slice = append(slice, o) 129 | } 130 | return slice, nil 131 | case map[string]interface{}: 132 | for k, v := range value { 133 | subPath := fmt.Sprintf("%s.%s", path, k) 134 | if d.filter(subPath, v) { 135 | continue 136 | } 137 | o, err := d.parse(v, k, subPath) 138 | if err != nil { 139 | return nil, fmt.Errorf("recursive call in map[]interface case: %v", err) 140 | } 141 | output[k] = o 142 | } 143 | default: 144 | log.Printf("unhandled type %T\n", value) 145 | } 146 | 147 | return output, nil 148 | } 149 | 150 | func (d *Delete) filter(path string, value interface{}) bool { 151 | switch { 152 | case d.Path != "" && d.Value != "": 153 | return d.filterPathAndValue(path, value) 154 | case d.Path != "": 155 | return d.filterPath(path) 156 | case d.Value != "": 157 | return d.filterValue(value) 158 | } 159 | // consider empty key and path as "delete any" 160 | return true 161 | } 162 | 163 | func (d *Delete) filterPath(path string) bool { 164 | return "."+d.Path == path 165 | } 166 | 167 | func (d *Delete) filterValue(value interface{}) bool { 168 | switch v := value.(type) { 169 | case string: 170 | return v == d.Value 171 | case float64: 172 | return d.Value == strconv.FormatFloat(v, 'f', -1, 64) 173 | case bool: 174 | return d.Value == fmt.Sprintf("%t", v) 175 | } 176 | return false 177 | } 178 | 179 | func (d *Delete) filterPathAndValue(path string, value interface{}) bool { 180 | return d.filterPath(path) && d.filterValue(value) 181 | } 182 | -------------------------------------------------------------------------------- /pkg/pipeline/transformer/shift/shift.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 TriggerMesh Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package shift 18 | 19 | import ( 20 | "encoding/json" 21 | "strings" 22 | 23 | "github.com/triggermesh/bumblebee/pkg/pipeline/common/convert" 24 | "github.com/triggermesh/bumblebee/pkg/pipeline/common/storage" 25 | "github.com/triggermesh/bumblebee/pkg/pipeline/transformer" 26 | ) 27 | 28 | var _ transformer.Transformer = (*Shift)(nil) 29 | 30 | // Shift object implements Transformer interface. 31 | type Shift struct { 32 | Path string 33 | NewPath string 34 | Value string 35 | 36 | variables *storage.Storage 37 | } 38 | 39 | const delimeter string = ":" 40 | 41 | // InitStep is used to figure out if this operation should 42 | // run before main Transformations. For example, Store 43 | // operation needs to run first to load all Pipeline variables. 44 | var InitStep bool = false 45 | 46 | // operationName is used to identify this transformation. 47 | var operationName string = "shift" 48 | 49 | // Register adds this transformation to the map which will 50 | // be used to create Transformation pipeline. 51 | func Register(m map[string]transformer.Transformer) { 52 | m[operationName] = &Shift{} 53 | } 54 | 55 | // SetStorage sets a shared Storage with Pipeline variables. 56 | func (s *Shift) SetStorage(storage *storage.Storage) { 57 | s.variables = storage 58 | } 59 | 60 | // InitStep returns "true" if this Transformation should run 61 | // as init step. 62 | func (s *Shift) InitStep() bool { 63 | return InitStep 64 | } 65 | 66 | // New returns a new instance of Shift object. 67 | func (s *Shift) New(key, value string) transformer.Transformer { 68 | // doubtful scheme, review needed 69 | keys := strings.Split(key, delimeter) 70 | if len(keys) != 2 { 71 | return nil 72 | } 73 | return &Shift{ 74 | Path: keys[0], 75 | NewPath: keys[1], 76 | Value: value, 77 | 78 | variables: s.variables, 79 | } 80 | } 81 | 82 | // Apply is a main method of Transformation that moves existing 83 | // values to a new locations. 84 | func (s *Shift) Apply(data []byte) ([]byte, error) { 85 | oldPath := convert.SliceToMap(strings.Split(s.Path, "."), "") 86 | 87 | var event interface{} 88 | if err := json.Unmarshal(data, &event); err != nil { 89 | return data, err 90 | } 91 | 92 | newEvent, value := extractValue(event, oldPath) 93 | if s.Value != "" { 94 | if !equal(s.retrieveInterface(s.Value), value) { 95 | return data, nil 96 | } 97 | } 98 | 99 | newPath := convert.SliceToMap(strings.Split(s.NewPath, "."), value) 100 | 101 | result := convert.MergeJSONWithMap(newEvent, newPath) 102 | output, err := json.Marshal(result) 103 | if err != nil { 104 | return data, err 105 | } 106 | 107 | return output, nil 108 | } 109 | 110 | func (s *Shift) retrieveInterface(key string) interface{} { 111 | if value := s.variables.Get(key); value != nil { 112 | return value 113 | } 114 | return key 115 | } 116 | 117 | func extractValue(source interface{}, path map[string]interface{}) (map[string]interface{}, interface{}) { 118 | var ok bool 119 | var result interface{} 120 | sourceMap := make(map[string]interface{}) 121 | for k, v := range path { 122 | switch value := v.(type) { 123 | case float64, bool, string: 124 | sourceMap, ok = source.(map[string]interface{}) 125 | if !ok { 126 | break 127 | } 128 | result = sourceMap[k] 129 | delete(sourceMap, k) 130 | case []interface{}: 131 | if k != "" { 132 | // array is inside the object 133 | // {"foo":[{},{},{}]} 134 | sourceMap, ok = source.(map[string]interface{}) 135 | if !ok { 136 | break 137 | } 138 | source, ok = sourceMap[k] 139 | if !ok { 140 | break 141 | } 142 | } 143 | // array is a root object 144 | // [{},{},{}] 145 | sourceArr, ok := source.([]interface{}) 146 | if !ok { 147 | break 148 | } 149 | 150 | index := len(value) - 1 151 | if index >= len(sourceArr) { 152 | break 153 | } 154 | 155 | m, ok := value[index].(map[string]interface{}) 156 | if ok { 157 | sourceArr[index], result = extractValue(sourceArr[index].(map[string]interface{}), m) 158 | sourceMap[k] = sourceArr 159 | break 160 | } 161 | result = sourceArr[index] 162 | sourceMap[k] = sourceArr[:index] 163 | if len(sourceArr) > index { 164 | sourceMap[k] = append(sourceArr[:index], sourceArr[index+1:]...) 165 | } 166 | case map[string]interface{}: 167 | sourceMap, ok = source.(map[string]interface{}) 168 | if !ok { 169 | break 170 | } 171 | if _, ok := sourceMap[k]; !ok { 172 | break 173 | } 174 | sourceMap[k], result = extractValue(sourceMap[k], value) 175 | case nil: 176 | sourceMap[k] = nil 177 | } 178 | } 179 | return sourceMap, result 180 | } 181 | 182 | func equal(a, b interface{}) bool { 183 | switch value := b.(type) { 184 | case string: 185 | v, ok := a.(string) 186 | if ok && v == value { 187 | return true 188 | } 189 | case bool: 190 | v, ok := a.(bool) 191 | if ok && v == value { 192 | return true 193 | } 194 | case float64: 195 | v, ok := a.(float64) 196 | if ok && v == value { 197 | return true 198 | } 199 | } 200 | return false 201 | } 202 | -------------------------------------------------------------------------------- /pkg/pipeline/transformer/store/store.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 TriggerMesh Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package store 18 | 19 | import ( 20 | "encoding/json" 21 | "strings" 22 | 23 | "github.com/triggermesh/bumblebee/pkg/pipeline/common/convert" 24 | "github.com/triggermesh/bumblebee/pkg/pipeline/common/storage" 25 | "github.com/triggermesh/bumblebee/pkg/pipeline/transformer" 26 | ) 27 | 28 | var _ transformer.Transformer = (*Store)(nil) 29 | 30 | // Store object implements Transformer interface. 31 | type Store struct { 32 | Path string 33 | Value string 34 | 35 | variables *storage.Storage 36 | } 37 | 38 | // InitStep is used to figure out if this operation should 39 | // run before main Transformations. For example, Store 40 | // operation needs to run first to load all Pipeline variables. 41 | var InitStep bool = true 42 | 43 | // operationName is used to identify this transformation. 44 | var operationName string = "store" 45 | 46 | // Register adds this transformation to the map which will 47 | // be used to create Transformation pipeline. 48 | func Register(m map[string]transformer.Transformer) { 49 | m[operationName] = &Store{} 50 | } 51 | 52 | // SetStorage sets a shared Storage with Pipeline variables. 53 | func (s *Store) SetStorage(storage *storage.Storage) { 54 | s.variables = storage 55 | } 56 | 57 | // InitStep returns "true" if this Transformation should run 58 | // as init step. 59 | func (s *Store) InitStep() bool { 60 | return InitStep 61 | } 62 | 63 | // New returns a new instance of Store object. 64 | func (s *Store) New(key, value string) transformer.Transformer { 65 | return &Store{ 66 | Path: key, 67 | Value: value, 68 | 69 | variables: s.variables, 70 | } 71 | } 72 | 73 | // Apply is a main method of Transformation that stores JSON values 74 | // into variables that can be used by other Transformations in a pipeline. 75 | func (s *Store) Apply(data []byte) ([]byte, error) { 76 | path := convert.SliceToMap(strings.Split(s.Value, "."), "") 77 | 78 | var event interface{} 79 | if err := json.Unmarshal(data, &event); err != nil { 80 | return data, err 81 | } 82 | 83 | value := readValue(event, path) 84 | s.variables.Set(s.Path, value) 85 | 86 | return data, nil 87 | } 88 | 89 | func readValue(source interface{}, path map[string]interface{}) interface{} { 90 | var result interface{} 91 | for k, v := range path { 92 | switch value := v.(type) { 93 | case float64, bool, string: 94 | sourceMap, ok := source.(map[string]interface{}) 95 | if !ok { 96 | break 97 | } 98 | result = sourceMap[k] 99 | case []interface{}: 100 | if k != "" { 101 | // array is inside the object 102 | // {"foo":[{},{},{}]} 103 | sourceMap, ok := source.(map[string]interface{}) 104 | if !ok { 105 | break 106 | } 107 | source, ok = sourceMap[k] 108 | if !ok { 109 | break 110 | } 111 | } 112 | // array is a root object 113 | // [{},{},{}] 114 | sourceArr, ok := source.([]interface{}) 115 | if !ok { 116 | break 117 | } 118 | 119 | index := len(value) - 1 120 | if index >= len(sourceArr) { 121 | break 122 | } 123 | result = readValue(sourceArr[index], value[index].(map[string]interface{})) 124 | case map[string]interface{}: 125 | sourceMap, ok := source.(map[string]interface{}) 126 | if !ok { 127 | break 128 | } 129 | if _, ok := sourceMap[k]; !ok { 130 | break 131 | } 132 | result = readValue(sourceMap[k], value) 133 | } 134 | } 135 | return result 136 | } 137 | -------------------------------------------------------------------------------- /pkg/pipeline/transformer/transformer.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 TriggerMesh Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package transformer 18 | 19 | import ( 20 | "github.com/triggermesh/bumblebee/pkg/pipeline/common/storage" 21 | ) 22 | 23 | // Transformer is an interface that contains common methods 24 | // to work with JSON data. 25 | type Transformer interface { 26 | New(string, string) Transformer 27 | Apply([]byte) ([]byte, error) 28 | SetStorage(*storage.Storage) 29 | InitStep() bool 30 | } 31 | -------------------------------------------------------------------------------- /pkg/reconciler/controller/controller.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Triggermesh Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package controller 18 | 19 | import ( 20 | "context" 21 | 22 | "github.com/kelseyhightower/envconfig" 23 | 24 | "knative.dev/pkg/configmap" 25 | "knative.dev/pkg/controller" 26 | "knative.dev/pkg/logging" 27 | "knative.dev/pkg/resolver" 28 | "knative.dev/pkg/tracker" 29 | servingv1 "knative.dev/serving/pkg/apis/serving/v1" 30 | servingv1client "knative.dev/serving/pkg/client/injection/client" 31 | knsvcinformer "knative.dev/serving/pkg/client/injection/informers/serving/v1/service" 32 | 33 | transformationinformer "github.com/triggermesh/bumblebee/pkg/client/generated/injection/informers/transformation/v1alpha1/transformation" 34 | transformationreconciler "github.com/triggermesh/bumblebee/pkg/client/generated/injection/reconciler/transformation/v1alpha1/transformation" 35 | ) 36 | 37 | type envConfig struct { 38 | Image string `envconfig:"TRANSFORMER_IMAGE" required:"true"` 39 | } 40 | 41 | // NewController creates a Reconciler and returns the result of NewImpl. 42 | func NewController( 43 | ctx context.Context, 44 | cmw configmap.Watcher, 45 | ) *controller.Impl { 46 | logger := logging.FromContext(ctx) 47 | 48 | transformationInformer := transformationinformer.Get(ctx) 49 | knsvcInformer := knsvcinformer.Get(ctx) 50 | 51 | r := &Reconciler{ 52 | servingClientSet: servingv1client.Get(ctx), 53 | knServiceLister: knsvcInformer.Lister(), 54 | } 55 | 56 | env := &envConfig{} 57 | if err := envconfig.Process("", env); err != nil { 58 | logger.Panicf("unable to process Transformation required environment variables: %v", err) 59 | } 60 | if env.Image == "" { 61 | logger.Panic("unable to process Transformation required environment variables (missing TRANSFORMER_IMAGE)") 62 | } 63 | 64 | r.transformerImage = env.Image 65 | 66 | impl := transformationreconciler.NewImpl(ctx, r) 67 | r.Tracker = tracker.New(impl.EnqueueKey, controller.GetTrackerLease(ctx)) 68 | 69 | r.sinkResolver = resolver.NewURIResolver(ctx, impl.EnqueueKey) 70 | 71 | logger.Info("Setting up event handlers.") 72 | 73 | transformationInformer.Informer().AddEventHandler(controller.HandleAll(impl.Enqueue)) 74 | 75 | knsvcInformer.Informer().AddEventHandler(controller.HandleAll( 76 | // Call the tracker's OnChanged method, but we've seen the objects 77 | // coming through this path missing TypeMeta, so ensure it is properly 78 | // populated. 79 | controller.EnsureTypeMeta( 80 | r.Tracker.OnChanged, 81 | servingv1.SchemeGroupVersion.WithKind("Service"), 82 | ), 83 | )) 84 | 85 | return impl 86 | } 87 | -------------------------------------------------------------------------------- /pkg/reconciler/controller/resources/knservice.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 TriggerMesh Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package resources 18 | 19 | import ( 20 | corev1 "k8s.io/api/core/v1" 21 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 22 | 23 | network "knative.dev/networking/pkg" 24 | "knative.dev/pkg/kmeta" 25 | serving "knative.dev/serving/pkg/apis/serving" 26 | servingv1 "knative.dev/serving/pkg/apis/serving/v1" 27 | ) 28 | 29 | const defaultContainerName = "transformer" 30 | 31 | // Option sets kn service options 32 | type Option func(*servingv1.Service) 33 | 34 | // NewKnService creates a Knative Service object. 35 | func NewKnService(ns, name string, opts ...Option) *servingv1.Service { 36 | d := &servingv1.Service{ 37 | ObjectMeta: metav1.ObjectMeta{ 38 | Namespace: ns, 39 | Name: name, 40 | }, 41 | } 42 | 43 | for _, opt := range opts { 44 | opt(d) 45 | } 46 | 47 | // ensure the container name is not empty 48 | containers := d.Spec.Template.Spec.Containers 49 | if len(containers) == 1 && containers[0].Name == "" { 50 | containers[0].Name = defaultContainerName 51 | } 52 | 53 | return d 54 | } 55 | 56 | // Image sets a Container's image. 57 | func Image(img string) Option { 58 | return func(svc *servingv1.Service) { 59 | image := &firstContainer(svc).Image 60 | *image = img 61 | } 62 | } 63 | 64 | // EnvVars sets the value of multiple environment variables. 65 | func EnvVars(evs ...corev1.EnvVar) Option { 66 | return func(svc *servingv1.Service) { 67 | svcEnvVars := envVarsFrom(svc) 68 | *svcEnvVars = append(*svcEnvVars, evs...) 69 | } 70 | } 71 | 72 | // EnvVar sets the value of a Container's environment variable. 73 | func EnvVar(name, val string) Option { 74 | return func(svc *servingv1.Service) { 75 | setEnvVar(envVarsFrom(svc), name, val, nil) 76 | } 77 | } 78 | 79 | func Owner(o kmeta.OwnerRefable) Option { 80 | return func(svc *servingv1.Service) { 81 | svc.SetOwnerReferences([]metav1.OwnerReference{ 82 | *kmeta.NewControllerRef(o), 83 | }) 84 | } 85 | } 86 | 87 | func envVarsFrom(svc *servingv1.Service) *[]corev1.EnvVar { 88 | return &firstContainer(svc).Env 89 | } 90 | 91 | func setEnvVar(envVars *[]corev1.EnvVar, name, value string, valueFrom *corev1.EnvVarSource) { 92 | *envVars = append(*envVars, corev1.EnvVar{ 93 | Name: name, 94 | Value: value, 95 | ValueFrom: valueFrom, 96 | }) 97 | } 98 | 99 | // firstContainer returns a PodSpecable's first Container definition. 100 | // A new empty Container is injected if the PodSpecable does not contain any. 101 | func firstContainer(svc *servingv1.Service) *corev1.Container { 102 | containers := &svc.Spec.Template.Spec.Containers 103 | if len(*containers) == 0 { 104 | *containers = make([]corev1.Container, 1) 105 | } 106 | return &(*containers)[0] 107 | } 108 | 109 | // KsvcLabelVisibilityClusterLocal sets label to avoid exposing the service externally. 110 | func KsvcLabelVisibilityClusterLocal() Option { 111 | return func(svc *servingv1.Service) { 112 | if svc.Labels != nil { 113 | svc.Labels[network.VisibilityLabelKey] = serving.VisibilityClusterLocal 114 | return 115 | } 116 | labels := map[string]string{ 117 | network.VisibilityLabelKey: serving.VisibilityClusterLocal, 118 | } 119 | svc.Labels = labels 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /pkg/reconciler/controller/transformation.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Triggermesh Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package controller 18 | 19 | import ( 20 | "context" 21 | "encoding/json" 22 | "fmt" 23 | "reflect" 24 | 25 | "go.uber.org/zap" 26 | corev1 "k8s.io/api/core/v1" 27 | apierrs "k8s.io/apimachinery/pkg/api/errors" 28 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 29 | 30 | "knative.dev/pkg/apis" 31 | duckv1 "knative.dev/pkg/apis/duck/v1" 32 | "knative.dev/pkg/logging" 33 | "knative.dev/pkg/network" 34 | "knative.dev/pkg/reconciler" 35 | "knative.dev/pkg/resolver" 36 | "knative.dev/pkg/tracker" 37 | servingv1 "knative.dev/serving/pkg/apis/serving/v1" 38 | servingv1client "knative.dev/serving/pkg/client/clientset/versioned" 39 | servingv1listers "knative.dev/serving/pkg/client/listers/serving/v1" 40 | 41 | transformationv1alpha1 "github.com/triggermesh/bumblebee/pkg/apis/transformation/v1alpha1" 42 | transformationreconciler "github.com/triggermesh/bumblebee/pkg/client/generated/injection/reconciler/transformation/v1alpha1/transformation" 43 | "github.com/triggermesh/bumblebee/pkg/reconciler/controller/resources" 44 | ) 45 | 46 | const ( 47 | envSink = "K_SINK" 48 | envTransformationCtx = "TRANSFORMATION_CONTEXT" 49 | envTransformationData = "TRANSFORMATION_DATA" 50 | ) 51 | 52 | // newReconciledNormal makes a new reconciler event with event type Normal, and 53 | // reason AddressableServiceReconciled. 54 | func newReconciledNormal(namespace, name string) reconciler.Event { 55 | return reconciler.NewEvent(corev1.EventTypeNormal, "TransformationReconciled", "Transformation reconciled: \"%s/%s\"", namespace, name) 56 | } 57 | 58 | // Reconciler implements addressableservicereconciler.Interface for 59 | // Transformation resources. 60 | type Reconciler struct { 61 | // Tracker builds an index of what resources are watching other resources 62 | // so that we can immediately react to changes tracked resources. 63 | Tracker tracker.Interface 64 | 65 | // Listers index properties about resources 66 | knServiceLister servingv1listers.ServiceLister 67 | servingClientSet servingv1client.Interface 68 | 69 | sinkResolver *resolver.URIResolver 70 | 71 | transformerImage string 72 | } 73 | 74 | // Check that our Reconciler implements Interface 75 | var _ transformationreconciler.Interface = (*Reconciler)(nil) 76 | 77 | // ReconcileKind implements Interface.ReconcileKind. 78 | func (r *Reconciler) ReconcileKind(ctx context.Context, trn *transformationv1alpha1.Transformation) reconciler.Event { 79 | logger := logging.FromContext(ctx) 80 | 81 | if err := r.Tracker.TrackReference(tracker.Reference{ 82 | APIVersion: "serving.knative.dev/v1", 83 | Kind: "Service", 84 | Name: trn.Name, 85 | Namespace: trn.Namespace, 86 | }, trn); err != nil { 87 | logger.Errorf("Error tracking service %s: %v", trn.Name, err) 88 | return err 89 | } 90 | 91 | // Reconcile Transformation Adapter 92 | ksvc, err := r.reconcileKnService(ctx, trn) 93 | if err != nil { 94 | logger.Error("Error reconciling Kn Service", zap.Error(err)) 95 | trn.Status.MarkServiceUnavailable(trn.Name) 96 | return err 97 | } 98 | 99 | if ksvc.IsReady() { 100 | trn.Status.Address = &duckv1.Addressable{ 101 | URL: &apis.URL{ 102 | Scheme: "http", 103 | Host: network.GetServiceHostname(trn.Name, trn.Namespace), 104 | }, 105 | } 106 | trn.Status.MarkServiceAvailable() 107 | } 108 | trn.Status.CloudEventAttributes = r.createCloudEventAttributes(&trn.Spec) 109 | 110 | logger.Debug("Transformation reconciled") 111 | return newReconciledNormal(trn.Namespace, trn.Name) 112 | } 113 | 114 | func (r *Reconciler) reconcileKnService(ctx context.Context, trn *transformationv1alpha1.Transformation) (*servingv1.Service, error) { 115 | logger := logging.FromContext(ctx) 116 | 117 | var sink string 118 | if trn.Spec.Sink != (duckv1.Destination{}) { 119 | uri, err := r.resolveDestination(ctx, trn) 120 | if err != nil { 121 | return nil, fmt.Errorf("cannot resolve Sink destination: %w", err) 122 | } 123 | trn.Status.SinkURI = uri 124 | sink = uri.String() 125 | } 126 | 127 | trnContext, err := json.Marshal(trn.Spec.Context) 128 | if err != nil { 129 | return nil, fmt.Errorf("cannot marshal context transformation spec: %w", err) 130 | } 131 | 132 | trnData, err := json.Marshal(trn.Spec.Data) 133 | if err != nil { 134 | return nil, fmt.Errorf("cannot marshal data transformation spec: %w", err) 135 | } 136 | 137 | expectedKsvc := resources.NewKnService(trn.Namespace, trn.Name, 138 | resources.Image(r.transformerImage), 139 | resources.EnvVar(envTransformationCtx, string(trnContext)), 140 | resources.EnvVar(envTransformationData, string(trnData)), 141 | resources.EnvVar(envSink, sink), 142 | resources.KsvcLabelVisibilityClusterLocal(), 143 | resources.Owner(trn), 144 | ) 145 | 146 | ksvc, err := r.knServiceLister.Services(trn.Namespace).Get(trn.Name) 147 | if apierrs.IsNotFound(err) { 148 | logger.Infof("Creating Kn Service %q", trn.Name) 149 | return r.servingClientSet.ServingV1().Services(trn.Namespace).Create(ctx, expectedKsvc, v1.CreateOptions{}) 150 | } else if err != nil { 151 | return nil, err 152 | } 153 | 154 | if !reflect.DeepEqual(ksvc.Spec.ConfigurationSpec.Template.Spec, 155 | expectedKsvc.Spec.ConfigurationSpec.Template.Spec) { 156 | ksvc.Spec = expectedKsvc.Spec 157 | return r.servingClientSet.ServingV1().Services(trn.Namespace).Update(ctx, ksvc, v1.UpdateOptions{}) 158 | } 159 | return ksvc, nil 160 | } 161 | 162 | func (r *Reconciler) createCloudEventAttributes(ts *transformationv1alpha1.TransformationSpec) []duckv1.CloudEventAttributes { 163 | ceAttributes := make([]duckv1.CloudEventAttributes, 0) 164 | for _, item := range ts.Context { 165 | if item.Operation == "add" { 166 | attribute := duckv1.CloudEventAttributes{} 167 | for _, path := range item.Paths { 168 | switch path.Key { 169 | case "type": 170 | attribute.Type = path.Value 171 | case "source": 172 | attribute.Source = path.Value 173 | } 174 | } 175 | if attribute.Source != "" || attribute.Type != "" { 176 | ceAttributes = append(ceAttributes, attribute) 177 | } 178 | break 179 | } 180 | } 181 | return ceAttributes 182 | } 183 | 184 | func (r *Reconciler) resolveDestination(ctx context.Context, trn *transformationv1alpha1.Transformation) (*apis.URL, error) { 185 | dest := trn.Spec.Sink.DeepCopy() 186 | if dest.Ref != nil { 187 | if dest.Ref.Namespace == "" { 188 | dest.Ref.Namespace = trn.GetNamespace() 189 | } 190 | } 191 | return r.sinkResolver.URIFromDestinationV1(ctx, *dest, trn) 192 | } 193 | --------------------------------------------------------------------------------