mirror of
				https://github.com/1Password/onepassword-operator.git
				synced 2025-10-31 11:49:40 +00:00 
			
		
		
		
	Compare commits
	
		
			40 Commits
		
	
	
		
			v1.0.0
			...
			release/v1
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 72cad7284c | ||
|   | 6043e0da0b | ||
|   | 753cc5e9a3 | ||
|   | 8cfe98073e | ||
|   | 96b42e7c52 | ||
|   | 579b5848da | ||
|   | b50d864b50 | ||
|   | 1643385d9b | ||
|   | 9441214733 | ||
|   | 7e4e988813 | ||
|   | 68f084080e | ||
|   | 859c9e3462 | ||
|   | 9dabac4a55 | ||
|   | d927a08790 | ||
|   | 933f7c4e2c | ||
|   | 81eb9a521f | ||
|   | eb32bd7f94 | ||
|   | a5781af949 | ||
|   | 0aa5781acd | ||
|   | 700be4426f | ||
|   | 76ef9aa372 | ||
|   | d7e6704314 | ||
|   | 2443979602 | ||
|   | 5b65196d31 | ||
|   | e7df8a485d | ||
|   | ded76138da | ||
|   | a5db6aeb81 | ||
|   | d45f682c37 | ||
|   | d0c1235e58 | ||
|   | 9e8f621020 | ||
|   | 8dd7a28456 | ||
|   | 43b06dd7aa | ||
|   | e8e01d6578 | ||
|   | b53e017b77 | ||
|   | b2565cebf8 | ||
|   | 9459d2e292 | ||
|   | 0409b17ef4 | ||
|   | 2e47b76d4c | ||
|   | 1cca09df90 | ||
|   | b9cb92eb1b | 
							
								
								
									
										36
									
								
								.github/ISSUE_TEMPLATE/bug_report.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								.github/ISSUE_TEMPLATE/bug_report.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | |||||||
|  | --- | ||||||
|  | name: Bug report | ||||||
|  | about: Report bugs and errors found while using the Operator. | ||||||
|  | title: '' | ||||||
|  | labels: bug | ||||||
|  | assignees: '' | ||||||
|  |  | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | ### Your environment | ||||||
|  |  | ||||||
|  | <!-- Version of the Operator when the error occurred --> | ||||||
|  | Operator Version: | ||||||
|  |  | ||||||
|  | <!-- What version of the Connect server are you running? | ||||||
|  | You can get this information from the Integrations section in 1Password | ||||||
|  | https://start.1password.com/integrations/active | ||||||
|  | --> | ||||||
|  | Connect Server Version: | ||||||
|  |  | ||||||
|  | <!-- What version of Kubernetes have you deployed the operator to? --> | ||||||
|  | Kubernetes Version: | ||||||
|  |  | ||||||
|  | ## What happened? | ||||||
|  | <!-- Describe the bug or error --> | ||||||
|  |  | ||||||
|  | ## What did you expect to happen? | ||||||
|  | <!-- Describe what should have happened --> | ||||||
|  |  | ||||||
|  | ## Steps to reproduce | ||||||
|  | 1. <!-- Describe Steps to reproduce the issue --> | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ## Notes & Logs | ||||||
|  | <!-- Paste any logs here that may help with debugging. | ||||||
|  | Remember to remove any sensitive information before sharing! --> | ||||||
							
								
								
									
										9
									
								
								.github/ISSUE_TEMPLATE/config.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								.github/ISSUE_TEMPLATE/config.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | # docs: https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/configuring-issue-templates-for-your-repository#configuring-the-template-chooser | ||||||
|  | blank_issues_enabled: true | ||||||
|  | contact_links: | ||||||
|  |   - name: 1Password Community | ||||||
|  |     url: https://1password.community/categories/secrets-automation | ||||||
|  |     about: Please ask general Secrets Automation questions here. | ||||||
|  |   - name: 1Password Security Bug Bounty | ||||||
|  |     url: https://bugcrowd.com/agilebits | ||||||
|  |     about: Please report security vulnerabilities here. | ||||||
							
								
								
									
										32
									
								
								.github/ISSUE_TEMPLATE/feature_request.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								.github/ISSUE_TEMPLATE/feature_request.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | |||||||
|  | --- | ||||||
|  | name: Feature request | ||||||
|  | about: Suggest an idea for the Operator | ||||||
|  | title: '' | ||||||
|  | labels: feature-request | ||||||
|  | assignees: '' | ||||||
|  |  | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | ### Summary | ||||||
|  | <!-- Briefly describe the feature in one or two sentences. You can include more details later. --> | ||||||
|  |  | ||||||
|  | ### Use cases | ||||||
|  | <!-- Describe the use cases that make this feature useful to others. | ||||||
|  | The description should help the reader understand why the feature is necessary. | ||||||
|  | The better we understand your use case, the better we can help create an appropriate solution. --> | ||||||
|  |  | ||||||
|  | ### Proposed solution | ||||||
|  | <!-- If you already have an idea for how the feature should work, use this space to describe it. | ||||||
|  | We'll work with you to find a workable approach, and any implementation details are appreciated. | ||||||
|  | --> | ||||||
|  |  | ||||||
|  | ### Is there a workaround to accomplish this today? | ||||||
|  | <!-- If there's a way to accomplish this feature request without changes to the codebase, we'd like to hear it. | ||||||
|  | --> | ||||||
|  |  | ||||||
|  | ### References & Prior Work | ||||||
|  | <!-- If a similar feature was implemented in another project or tool, add a link so we can better understand your request. | ||||||
|  | Links to relevant documentation or RFCs are also appreciated. --> | ||||||
|  |  | ||||||
|  | * <!-- Reference 1 --> | ||||||
|  | * <!-- Reference 2, etc --> | ||||||
							
								
								
									
										11
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										11
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,20 +1,11 @@ | |||||||
| name: Build and Test | name: Build and Test | ||||||
|  | on: [push, pull_request] | ||||||
| on: |  | ||||||
|   push: |  | ||||||
|     branches: |  | ||||||
|       - '**' |  | ||||||
|   pull_request: |  | ||||||
|     branches: |  | ||||||
|       - $default-branch |  | ||||||
|  |  | ||||||
| jobs: | jobs: | ||||||
|  |  | ||||||
|   build: |   build: | ||||||
|     name: Build |     name: Build | ||||||
|     runs-on: ubuntu-latest |     runs-on: ubuntu-latest | ||||||
|     steps: |     steps: | ||||||
|  |  | ||||||
|     - name: Set up Go 1.x |     - name: Set up Go 1.x | ||||||
|       uses: actions/setup-go@v2 |       uses: actions/setup-go@v2 | ||||||
|       with: |       with: | ||||||
|   | |||||||
							
								
								
									
										57
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,57 @@ | |||||||
|  | name: release | ||||||
|  |  | ||||||
|  | on: | ||||||
|  |   push: | ||||||
|  |     tags: | ||||||
|  |       - 'v*' | ||||||
|  |  | ||||||
|  | jobs: | ||||||
|  |   release-docker: | ||||||
|  |     runs-on: ubuntu-latest | ||||||
|  |     env: | ||||||
|  |       DOCKER_CLI_EXPERIMENTAL: "enabled" | ||||||
|  |     steps: | ||||||
|  |       - | ||||||
|  |         name: Checkout | ||||||
|  |         uses: actions/checkout@v2 | ||||||
|  |         with: | ||||||
|  |           fetch-depth: 0 | ||||||
|  |       - | ||||||
|  |         name: Docker meta | ||||||
|  |         id: meta | ||||||
|  |         uses: crazy-max/ghaction-docker-meta@v2 | ||||||
|  |         with: | ||||||
|  |           images: | | ||||||
|  |             1password/onepassword-operator | ||||||
|  |           # Publish image for x.y.z and x.y | ||||||
|  |           # The latest tag is automatically added for semver tags | ||||||
|  |           tags: | | ||||||
|  |             type=semver,pattern={{version}} | ||||||
|  |             type=semver,pattern={{major}}.{{minor}} | ||||||
|  |       - name: Get the version from tag | ||||||
|  |         id: get_version | ||||||
|  |         run: echo ::set-output name=VERSION::${GITHUB_REF#refs/tags/v} | ||||||
|  |       - | ||||||
|  |         name: Set up QEMU | ||||||
|  |         uses: docker/setup-qemu-action@v1 | ||||||
|  |       - | ||||||
|  |         name: Set up Docker Buildx | ||||||
|  |         uses: docker/setup-buildx-action@v1 | ||||||
|  |       - | ||||||
|  |         name: Docker Login | ||||||
|  |         uses: docker/login-action@v1 | ||||||
|  |         with: | ||||||
|  |           username: ${{ secrets.DOCKERHUB_USERNAME }} | ||||||
|  |           password: ${{ secrets.DOCKERHUB_TOKEN }} | ||||||
|  |       - | ||||||
|  |         name: Build and push | ||||||
|  |         uses: docker/build-push-action@v2 | ||||||
|  |         with: | ||||||
|  |           context: . | ||||||
|  |           file: Dockerfile | ||||||
|  |           platforms: linux/amd64,linux/arm64,linux/arm/v7 | ||||||
|  |           push: true | ||||||
|  |           tags: ${{ steps.meta.outputs.tags }} | ||||||
|  |           labels: ${{ steps.meta.outputs.labels }} | ||||||
|  |           build-args: | | ||||||
|  |             operator_version=${{ steps.get_version.outputs.VERSION }} | ||||||
							
								
								
									
										17
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								CHANGELOG.md
									
									
									
									
									
								
							| @@ -12,6 +12,23 @@ | |||||||
|  |  | ||||||
| --- | --- | ||||||
|  |  | ||||||
|  | [//]: # (START/v1.0.2) | ||||||
|  | # v1.0.2 | ||||||
|  |  | ||||||
|  | ## Fixes | ||||||
|  |  * Name normalizer added to handle non-conforming item names. | ||||||
|  |  | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | [//]: # (START/v1.0.1) | ||||||
|  | # v1.0.1 | ||||||
|  |  | ||||||
|  | ## Features | ||||||
|  | * This release also contains an arm64 Docker image. {#20} | ||||||
|  | * Docker images are also pushed to the :latest and :<major>.<minor> tags. | ||||||
|  |  | ||||||
|  | --- | ||||||
|  |  | ||||||
| [//]: # (START/v1.0.0) | [//]: # (START/v1.0.0) | ||||||
| # v1.0.0 | # v1.0.0 | ||||||
|  |  | ||||||
|   | |||||||
| @@ -14,11 +14,9 @@ COPY vendor/ vendor/ | |||||||
| # Build | # Build | ||||||
| ARG operator_version=dev | ARG operator_version=dev | ||||||
| RUN CGO_ENABLED=0 \ | RUN CGO_ENABLED=0 \ | ||||||
|     GOOS=linux \ |  | ||||||
|     GOARCH=amd64 \ |  | ||||||
|     GO111MODULE=on \ |     GO111MODULE=on \ | ||||||
|     go build \ |     go build \ | ||||||
|     -ldflags "-X version.Version=$operator_version" \ |     -ldflags "-X \"github.com/1Password/onepassword-operator/version.Version=$operator_version\"" \ | ||||||
|     -mod vendor \ |     -mod vendor \ | ||||||
|     -a -o manager main.go |     -a -o manager main.go | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										4
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								Makefile
									
									
									
									
									
								
							| @@ -20,12 +20,12 @@ test/coverage:	## Run test suite with coverage report | |||||||
| 	go test -v ./... -cover | 	go test -v ./... -cover | ||||||
|  |  | ||||||
| build:	## Build operator Docker image | build:	## Build operator Docker image | ||||||
| 	@docker build -f Dockerfile --build-arg operator_version=$(curVersion) -t $(DOCKER_IMG_TAG) | 	@docker build -f Dockerfile --build-arg operator_version=$(curVersion) -t $(DOCKER_IMG_TAG) . | ||||||
| 	@echo "Successfully built and tagged image." | 	@echo "Successfully built and tagged image." | ||||||
| 	@echo "Tag: $(DOCKER_IMG_TAG)" | 	@echo "Tag: $(DOCKER_IMG_TAG)" | ||||||
|  |  | ||||||
| build/local:	## Build local version of the operator Docker image | build/local:	## Build local version of the operator Docker image | ||||||
| 	@docker build -f Dockerfile -t local/$(DOCKER_IMG_TAG) | 	@docker build -f Dockerfile -t local/$(DOCKER_IMG_TAG) . | ||||||
|  |  | ||||||
| build/binary: clean	## Build operator binary | build/binary: clean	## Build operator binary | ||||||
| 	@mkdir -p dist | 	@mkdir -p dist | ||||||
|   | |||||||
							
								
								
									
										30
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								README.md
									
									
									
									
									
								
							| @@ -13,8 +13,8 @@ Prerequisites: | |||||||
| - [1Password Command Line Tool Installed](https://1password.com/downloads/command-line/) | - [1Password Command Line Tool Installed](https://1password.com/downloads/command-line/) | ||||||
| - [kubectl installed](https://kubernetes.io/docs/tasks/tools/install-kubectl/) | - [kubectl installed](https://kubernetes.io/docs/tasks/tools/install-kubectl/) | ||||||
| - [docker installed](https://docs.docker.com/get-docker/) | - [docker installed](https://docs.docker.com/get-docker/) | ||||||
| - [Generated a 1password-credentials.json file and issued a 1Password Connect API Token for the K8s Operator integration](https://support.b5dev.com/cs/connect) | - [Generated a 1password-credentials.json file and issued a 1Password Connect API Token for the K8s Operator integration](https://support.1password.com/secrets-automation/) | ||||||
| - [1Password Connect deployed to Kubernetes](https://support.b5dev.com/cs/connect-deploy-kubernetes/#step-2-deploy-a-connect-server). **NOTE**: If customization of the 1Password Connect deployment is not required you can skip this prerequisite. | - [1Password Connect deployed to Kubernetes](https://support.1password.com/connect-deploy-kubernetes/#step-2-deploy-a-1password-connect-server). **NOTE**: If customization of the 1Password Connect deployment is not required you can skip this prerequisite. | ||||||
|  |  | ||||||
| ### Quickstart for Deploying 1Password Connect to Kubernetes | ### Quickstart for Deploying 1Password Connect to Kubernetes | ||||||
|  |  | ||||||
| @@ -53,15 +53,15 @@ Adding this environment variable will have the operator automatically deploy a d | |||||||
| "Create a Connect token for the operator and save it as a Kubernetes Secret:  | "Create a Connect token for the operator and save it as a Kubernetes Secret:  | ||||||
|  |  | ||||||
| ```bash | ```bash | ||||||
| $ kubectl create secret generic op-operator-connect-token --from-literal=token=<OP_CONNECT_TOKEN>" | $ kubectl create secret generic onepassword-token --from-literal=token=<OP_CONNECT_TOKEN>" | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| If you do not have a token for the operator, you can generate a token and save it to kubernetes with the following command: | If you do not have a token for the operator, you can generate a token and save it to kubernetes with the following command: | ||||||
| ```bash | ```bash | ||||||
| $ kubectl create secret generic op-operator-connect-token --from-literal=token=$(op create connect token <server> op-k8s-operator --vault <vault>) | $ kubectl create secret generic onepassword-token --from-literal=token=$(op create connect token <server> op-k8s-operator --vault <vault>) | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| [More information on generating a token can be found here](https://support.1password.com/cs/secrets-automation/#appendix-issue-additional-access-tokens) | [More information on generating a token can be found here](https://support.1password.com/secrets-automation/#appendix-issue-additional-access-tokens) | ||||||
|  |  | ||||||
| **Set Permissions For Operator** | **Set Permissions For Operator** | ||||||
|  |  | ||||||
| @@ -84,9 +84,9 @@ An sample Deployment yaml can be found at `/deploy/operator.yaml`. | |||||||
|  |  | ||||||
| To further configure the 1Password Kubernetes Operator the Following Environment variables can be set in the operator yaml: | To further configure the 1Password Kubernetes Operator the Following Environment variables can be set in the operator yaml: | ||||||
|  |  | ||||||
| - **WATCH_NAMESPACE:** comma separated list of what Namespaces to watch for changes. |  | ||||||
| - **OP_CONNECT_HOST** (required): Specifies the host name within Kubernetes in which to access the 1Password Connect. | - **OP_CONNECT_HOST** (required): Specifies the host name within Kubernetes in which to access the 1Password Connect. | ||||||
| - **POLLING_INTERVAL** (default: 600)**:** The number of seconds the 1Password Kubernetes Operator will wait before checking for updates from 1Password Connect. | - **WATCH_NAMESPACE:** (default: watch all namespaces): Comma separated list of what Namespaces to watch for changes. | ||||||
|  | - **POLLING_INTERVAL** (default: 600): The number of seconds the 1Password Kubernetes Operator will wait before checking for updates from 1Password Connect. | ||||||
| - **MANAGE_CONNECT** (default: false): If set to true, on deployment of the operator, a default configuration of the OnePassword Connect Service will be deployed to the `default` namespace. | - **MANAGE_CONNECT** (default: false): If set to true, on deployment of the operator, a default configuration of the OnePassword Connect Service will be deployed to the `default` namespace. | ||||||
| - **AUTO_RESTART** (default: false): If set to true, the operator will restart any deployment using a secret from 1Password Connect. This can be overwritten by namespace, deployment, or individual secret. More details on AUTO_RESTART can be found in the ["Configuring Automatic Rolling Restarts of Deployments"](#configuring-automatic-rolling-restarts-of-deployments) section. | - **AUTO_RESTART** (default: false): If set to true, the operator will restart any deployment using a secret from 1Password Connect. This can be overwritten by namespace, deployment, or individual secret. More details on AUTO_RESTART can be found in the ["Configuring Automatic Rolling Restarts of Deployments"](#configuring-automatic-rolling-restarts-of-deployments) section. | ||||||
|  |  | ||||||
| @@ -102,7 +102,7 @@ To create a Kubernetes Secret from a 1Password item, create a yaml file with the | |||||||
|  |  | ||||||
| ```yaml | ```yaml | ||||||
| apiVersion: onepassword.com/v1 | apiVersion: onepassword.com/v1 | ||||||
| kind: OnePasswordItem # {insert_new_name} | kind: OnePasswordItem | ||||||
| metadata: | metadata: | ||||||
|   name: <item_name> #this name will also be used for naming the generated kubernetes secret |   name: <item_name> #this name will also be used for naming the generated kubernetes secret | ||||||
| spec: | spec: | ||||||
| @@ -131,8 +131,8 @@ kind: Deployment | |||||||
| metadata: | metadata: | ||||||
|   name: deployment-example |   name: deployment-example | ||||||
|   annotations: |   annotations: | ||||||
|     operator.1password.io/item-path: "vaults/{vault_id_or_title}/items/{item_id_or_title}" |     operator.1password.io/item-path: "vaults/<vault_id_or_title>/items/<item_id_or_title>" | ||||||
|     operator.1password.io/item-name: "{secret_name}" |     operator.1password.io/item-name: "<secret_name>" | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| Applying this yaml file will create a Kubernetes Secret with the name `<secret_name>` and contents from the location specified at the specified Item Path. | Applying this yaml file will create a Kubernetes Secret with the name `<secret_name>` and contents from the location specified at the specified Item Path. | ||||||
| @@ -144,7 +144,12 @@ If a 1Password Item that is linked to a Kubernetes Secret is updated within the | |||||||
| --- | --- | ||||||
| **NOTE** | **NOTE** | ||||||
|  |  | ||||||
| If multiple 1Password vaults/items have the same `title` when using a title in the access path, the desired action will be performed on the oldest vault/item. Furthermore, titles that include white space characters cannot be used. | If multiple 1Password vaults/items have the same `title` when using a title in the access path, the desired action will be performed on the oldest vault/item.  | ||||||
|  |  | ||||||
|  | Titles and field names that include white space and other characters that are not a valid [DNS subdomain name](https://kubernetes.io/docs/concepts/configuration/secret/) will create Kubernetes secrets that have titles and fields in the following format: | ||||||
|  |  - Invalid characters before the first alphanumeric character and after the last alphanumeric character will be removed | ||||||
|  |  - All whitespaces between words will be replaced by `-` | ||||||
|  |  - All the letters will be lower-cased. | ||||||
|  |  | ||||||
| --- | --- | ||||||
|  |  | ||||||
| @@ -163,6 +168,7 @@ apiVersion: v1 | |||||||
| kind: Namespace | kind: Namespace | ||||||
| metadata: | metadata: | ||||||
|   name: "example-namespace" |   name: "example-namespace" | ||||||
|  |   annotations: | ||||||
|     operator.1password.io/auto-restart: "true" |     operator.1password.io/auto-restart: "true" | ||||||
| ``` | ``` | ||||||
| If the value is not set, the auto reset settings on the operator will be used. This value can be overwritten by deployment. | If the value is not set, the auto reset settings on the operator will be used. This value can be overwritten by deployment. | ||||||
| @@ -175,6 +181,7 @@ apiVersion: v1 | |||||||
| kind: Deployment | kind: Deployment | ||||||
| metadata: | metadata: | ||||||
|   name: "example-deployment" |   name: "example-deployment" | ||||||
|  |   annotations: | ||||||
|     operator.1password.io/auto-restart: "true" |     operator.1password.io/auto-restart: "true" | ||||||
| ``` | ``` | ||||||
| If the value is not set, the auto reset settings on the namespace will be used. | If the value is not set, the auto reset settings on the namespace will be used. | ||||||
| @@ -187,6 +194,7 @@ apiVersion: onepassword.com/v1 | |||||||
| kind: OnePasswordItem | kind: OnePasswordItem | ||||||
| metadata: | metadata: | ||||||
|   name: example |   name: example | ||||||
|  |   annotations: | ||||||
|     operator.1password.io/auto-restart: "true" |     operator.1password.io/auto-restart: "true" | ||||||
| ``` | ``` | ||||||
| If the value is not set, the auto reset settings on the deployment will be used. | If the value is not set, the auto reset settings on the deployment will be used. | ||||||
|   | |||||||
| @@ -83,9 +83,11 @@ func main() { | |||||||
|  |  | ||||||
| 	printVersion() | 	printVersion() | ||||||
|  |  | ||||||
| 	namespace, err := k8sutil.GetWatchNamespace() | 	namespace := os.Getenv(k8sutil.WatchNamespaceEnvVar) | ||||||
|  |  | ||||||
|  | 	deploymentNamespace, err := k8sutil.GetOperatorNamespace() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Error(err, "Failed to get watch namespace") | 		log.Error(err, "Failed to get namespace") | ||||||
| 		os.Exit(1) | 		os.Exit(1) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -139,7 +141,7 @@ func main() { | |||||||
| 		go func() { | 		go func() { | ||||||
| 			connectStarted := false | 			connectStarted := false | ||||||
| 			for connectStarted == false { | 			for connectStarted == false { | ||||||
| 				err := op.SetupConnect(mgr.GetClient()) | 				err := op.SetupConnect(mgr.GetClient(), deploymentNamespace) | ||||||
| 				// Cache Not Started is an acceptable error. Retry until cache is started. | 				// Cache Not Started is an acceptable error. Retry until cache is started. | ||||||
| 				if err != nil && !errors.Is(err, &cache.ErrCacheNotStarted{}) { | 				if err != nil && !errors.Is(err, &cache.ErrCacheNotStarted{}) { | ||||||
| 					log.Error(err, "") | 					log.Error(err, "") | ||||||
|   | |||||||
| @@ -2,7 +2,6 @@ apiVersion: apps/v1 | |||||||
| kind: Deployment | kind: Deployment | ||||||
| metadata: | metadata: | ||||||
|   name: onepassword-connect |   name: onepassword-connect | ||||||
|   namespace: default |  | ||||||
| spec: | spec: | ||||||
|   selector: |   selector: | ||||||
|     matchLabels: |     matchLabels: | ||||||
|   | |||||||
| @@ -2,7 +2,6 @@ apiVersion: v1 | |||||||
| kind: Service | kind: Service | ||||||
| metadata: | metadata: | ||||||
|   name: onepassword-connect |   name: onepassword-connect | ||||||
|   namespace: default |  | ||||||
| spec: | spec: | ||||||
|   type: NodePort |   type: NodePort | ||||||
|   selector: |   selector: | ||||||
|   | |||||||
| @@ -3,7 +3,6 @@ package onepassworditem | |||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  |  | ||||||
| 	onepasswordv1 "github.com/1Password/onepassword-operator/pkg/apis/onepassword/v1" | 	onepasswordv1 "github.com/1Password/onepassword-operator/pkg/apis/onepassword/v1" | ||||||
| 	kubeSecrets "github.com/1Password/onepassword-operator/pkg/kubernetessecrets" | 	kubeSecrets "github.com/1Password/onepassword-operator/pkg/kubernetessecrets" | ||||||
| 	"github.com/1Password/onepassword-operator/pkg/onepassword" | 	"github.com/1Password/onepassword-operator/pkg/onepassword" | ||||||
|   | |||||||
| @@ -31,6 +31,9 @@ const ( | |||||||
| 	itemId                    = "nwrhuano7bcwddcviubpp4mhfq" | 	itemId                    = "nwrhuano7bcwddcviubpp4mhfq" | ||||||
| 	username                  = "test-user" | 	username                  = "test-user" | ||||||
| 	password                  = "QmHumKc$mUeEem7caHtbaBaJ" | 	password                  = "QmHumKc$mUeEem7caHtbaBaJ" | ||||||
|  | 	firstHost                 = "http://localhost:8080" | ||||||
|  | 	awsKey                    = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" | ||||||
|  | 	iceCream                  = "freezing blue 20%" | ||||||
| 	userKey                   = "username" | 	userKey                   = "username" | ||||||
| 	passKey                   = "password" | 	passKey                   = "password" | ||||||
| 	version                   = 123 | 	version                   = 123 | ||||||
| @@ -210,6 +213,79 @@ var tests = []testReconcileItem{ | |||||||
| 			passKey: password, | 			passKey: password, | ||||||
| 		}, | 		}, | ||||||
| 	}, | 	}, | ||||||
|  | 	{ | ||||||
|  | 		testName: "Secret from 1Password item with invalid K8s labels", | ||||||
|  | 		customResource: &onepasswordv1.OnePasswordItem{ | ||||||
|  | 			TypeMeta: metav1.TypeMeta{ | ||||||
|  | 				Kind:       onePasswordItemKind, | ||||||
|  | 				APIVersion: onePasswordItemAPIVersion, | ||||||
|  | 			}, | ||||||
|  | 			ObjectMeta: metav1.ObjectMeta{ | ||||||
|  | 				Name:      "!my sECReT it3m%", | ||||||
|  | 				Namespace: namespace, | ||||||
|  | 			}, | ||||||
|  | 			Spec: onepasswordv1.OnePasswordItemSpec{ | ||||||
|  | 				ItemPath: itemPath, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		existingSecret: nil, | ||||||
|  | 		expectedError:  nil, | ||||||
|  | 		expectedResultSecret: &corev1.Secret{ | ||||||
|  | 			ObjectMeta: metav1.ObjectMeta{ | ||||||
|  | 				Name:      "my-secret-it3m", | ||||||
|  | 				Namespace: namespace, | ||||||
|  | 				Annotations: map[string]string{ | ||||||
|  | 					op.VersionAnnotation: fmt.Sprint(version), | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 			Data: expectedSecretData, | ||||||
|  | 		}, | ||||||
|  | 		opItem: map[string]string{ | ||||||
|  | 			userKey: username, | ||||||
|  | 			passKey: password, | ||||||
|  | 		}, | ||||||
|  | 	}, | ||||||
|  | 	{ | ||||||
|  | 		testName: "Secret from 1Password item with fields and sections that have invalid K8s labels", | ||||||
|  | 		customResource: &onepasswordv1.OnePasswordItem{ | ||||||
|  | 			TypeMeta: metav1.TypeMeta{ | ||||||
|  | 				Kind:       onePasswordItemKind, | ||||||
|  | 				APIVersion: onePasswordItemAPIVersion, | ||||||
|  | 			}, | ||||||
|  | 			ObjectMeta: metav1.ObjectMeta{ | ||||||
|  | 				Name:      "!my sECReT it3m%", | ||||||
|  | 				Namespace: namespace, | ||||||
|  | 			}, | ||||||
|  | 			Spec: onepasswordv1.OnePasswordItemSpec{ | ||||||
|  | 				ItemPath: itemPath, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		existingSecret: nil, | ||||||
|  | 		expectedError:  nil, | ||||||
|  | 		expectedResultSecret: &corev1.Secret{ | ||||||
|  | 			ObjectMeta: metav1.ObjectMeta{ | ||||||
|  | 				Name:      "my-secret-it3m", | ||||||
|  | 				Namespace: namespace, | ||||||
|  | 				Annotations: map[string]string{ | ||||||
|  | 					op.VersionAnnotation: fmt.Sprint(version), | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 			Data: map[string][]byte{ | ||||||
|  | 				"password":       []byte(password), | ||||||
|  | 				"username":       []byte(username), | ||||||
|  | 				"first-host":     []byte(firstHost), | ||||||
|  | 				"aws-access-key": []byte(awsKey), | ||||||
|  | 				"ice-cream-type": []byte(iceCream), | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		opItem: map[string]string{ | ||||||
|  | 			userKey:            username, | ||||||
|  | 			passKey:            password, | ||||||
|  | 			"first host":       firstHost, | ||||||
|  | 			"AWS Access Key":   awsKey, | ||||||
|  | 			"😄 ice-cream type": iceCream, | ||||||
|  | 		}, | ||||||
|  | 	}, | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestReconcileOnePasswordItem(t *testing.T) { | func TestReconcileOnePasswordItem(t *testing.T) { | ||||||
| @@ -241,7 +317,10 @@ func TestReconcileOnePasswordItem(t *testing.T) { | |||||||
| 			mocks.GetGetItemFunc = func(uuid string, vaultUUID string) (*onepassword.Item, error) { | 			mocks.GetGetItemFunc = func(uuid string, vaultUUID string) (*onepassword.Item, error) { | ||||||
|  |  | ||||||
| 				item := onepassword.Item{} | 				item := onepassword.Item{} | ||||||
| 				item.Fields = generateFields(testData.opItem["username"], testData.opItem["password"]) | 				item.Fields = []*onepassword.ItemField{} | ||||||
|  | 				for k, v := range testData.opItem { | ||||||
|  | 					item.Fields = append(item.Fields, &onepassword.ItemField{Label: k, Value: v}) | ||||||
|  | 				} | ||||||
| 				item.Version = version | 				item.Version = version | ||||||
| 				item.Vault.ID = vaultUUID | 				item.Vault.ID = vaultUUID | ||||||
| 				item.ID = uuid | 				item.ID = uuid | ||||||
| @@ -257,8 +336,8 @@ func TestReconcileOnePasswordItem(t *testing.T) { | |||||||
| 			// watched resource . | 			// watched resource . | ||||||
| 			req := reconcile.Request{ | 			req := reconcile.Request{ | ||||||
| 				NamespacedName: types.NamespacedName{ | 				NamespacedName: types.NamespacedName{ | ||||||
| 					Name:      name, | 					Name:      testData.customResource.ObjectMeta.Name, | ||||||
| 					Namespace: namespace, | 					Namespace: testData.customResource.ObjectMeta.Namespace, | ||||||
| 				}, | 				}, | ||||||
| 			} | 			} | ||||||
| 			_, err := r.Reconcile(req) | 			_, err := r.Reconcile(req) | ||||||
|   | |||||||
| @@ -4,12 +4,17 @@ import ( | |||||||
| 	"context" | 	"context" | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  |  | ||||||
|  | 	"regexp" | ||||||
|  | 	"strings" | ||||||
|  |  | ||||||
| 	"github.com/1Password/connect-sdk-go/onepassword" | 	"github.com/1Password/connect-sdk-go/onepassword" | ||||||
| 	"github.com/1Password/onepassword-operator/pkg/utils" | 	"github.com/1Password/onepassword-operator/pkg/utils" | ||||||
| 	corev1 "k8s.io/api/core/v1" | 	corev1 "k8s.io/api/core/v1" | ||||||
| 	"k8s.io/apimachinery/pkg/api/errors" | 	"k8s.io/apimachinery/pkg/api/errors" | ||||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||||
| 	"k8s.io/apimachinery/pkg/types" | 	"k8s.io/apimachinery/pkg/types" | ||||||
|  | 	kubeValidate "k8s.io/apimachinery/pkg/util/validation" | ||||||
|  |  | ||||||
| 	kubernetesClient "sigs.k8s.io/controller-runtime/pkg/client" | 	kubernetesClient "sigs.k8s.io/controller-runtime/pkg/client" | ||||||
| 	logf "sigs.k8s.io/controller-runtime/pkg/log" | 	logf "sigs.k8s.io/controller-runtime/pkg/log" | ||||||
| ) | ) | ||||||
| @@ -63,7 +68,7 @@ func CreateKubernetesSecretFromItem(kubeClient kubernetesClient.Client, secretNa | |||||||
| func BuildKubernetesSecretFromOnePasswordItem(name, namespace string, annotations map[string]string, item onepassword.Item) *corev1.Secret { | func BuildKubernetesSecretFromOnePasswordItem(name, namespace string, annotations map[string]string, item onepassword.Item) *corev1.Secret { | ||||||
| 	return &corev1.Secret{ | 	return &corev1.Secret{ | ||||||
| 		ObjectMeta: metav1.ObjectMeta{ | 		ObjectMeta: metav1.ObjectMeta{ | ||||||
| 			Name:        name, | 			Name:        formatSecretName(name), | ||||||
| 			Namespace:   namespace, | 			Namespace:   namespace, | ||||||
| 			Annotations: annotations, | 			Annotations: annotations, | ||||||
| 		}, | 		}, | ||||||
| @@ -75,8 +80,33 @@ func BuildKubernetesSecretData(fields []*onepassword.ItemField) map[string][]byt | |||||||
| 	secretData := map[string][]byte{} | 	secretData := map[string][]byte{} | ||||||
| 	for i := 0; i < len(fields); i++ { | 	for i := 0; i < len(fields); i++ { | ||||||
| 		if fields[i].Value != "" { | 		if fields[i].Value != "" { | ||||||
| 			secretData[fields[i].Label] = []byte(fields[i].Value) | 			key := formatSecretName(fields[i].Label) | ||||||
|  | 			secretData[key] = []byte(fields[i].Value) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	return secretData | 	return secretData | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // formatSecretName rewrites a value to be a valid Secret name or Secret data key. | ||||||
|  | // | ||||||
|  | // The Secret meta.name and data keys must be valid DNS subdomain names (https://kubernetes.io/docs/concepts/configuration/secret/#overview-of-secrets) | ||||||
|  | func formatSecretName(value string) string { | ||||||
|  | 	if errs := kubeValidate.IsDNS1123Subdomain(value); len(errs) == 0 { | ||||||
|  | 		return value | ||||||
|  | 	} | ||||||
|  | 	return createValidSecretName(value) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | var invalidDNS1123Chars = regexp.MustCompile("[^a-z0-9-]+") | ||||||
|  |  | ||||||
|  | func createValidSecretName(value string) string { | ||||||
|  | 	result := strings.ToLower(value) | ||||||
|  | 	result = invalidDNS1123Chars.ReplaceAllString(result, "-") | ||||||
|  |  | ||||||
|  | 	if len(result) > kubeValidate.DNS1123SubdomainMaxLength { | ||||||
|  | 		result = result[0:kubeValidate.DNS1123SubdomainMaxLength] | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// first and last character MUST be alphanumeric | ||||||
|  | 	return strings.Trim(result, "-") | ||||||
|  | } | ||||||
|   | |||||||
| @@ -3,6 +3,7 @@ package kubernetessecrets | |||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  | 	kubeValidate "k8s.io/apimachinery/pkg/util/validation" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| @@ -101,7 +102,7 @@ func TestBuildKubernetesSecretFromOnePasswordItem(t *testing.T) { | |||||||
| 	item.Fields = generateFields(5) | 	item.Fields = generateFields(5) | ||||||
|  |  | ||||||
| 	kubeSecret := BuildKubernetesSecretFromOnePasswordItem(name, namespace, annotations, item) | 	kubeSecret := BuildKubernetesSecretFromOnePasswordItem(name, namespace, annotations, item) | ||||||
| 	if kubeSecret.Name != name { | 	if kubeSecret.Name != strings.ToLower(name) { | ||||||
| 		t.Errorf("Expected name value: %v but got: %v", name, kubeSecret.Name) | 		t.Errorf("Expected name value: %v but got: %v", name, kubeSecret.Name) | ||||||
| 	} | 	} | ||||||
| 	if kubeSecret.Namespace != namespace { | 	if kubeSecret.Namespace != namespace { | ||||||
| @@ -113,6 +114,44 @@ func TestBuildKubernetesSecretFromOnePasswordItem(t *testing.T) { | |||||||
| 	compareFields(item.Fields, kubeSecret.Data, t) | 	compareFields(item.Fields, kubeSecret.Data, t) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func TestBuildKubernetesSecretFixesInvalidLabels(t *testing.T) { | ||||||
|  | 	name := "inV@l1d k8s secret%name" | ||||||
|  | 	expectedName := "inv-l1d-k8s-secret-name" | ||||||
|  | 	namespace := "someNamespace" | ||||||
|  | 	annotations := map[string]string{ | ||||||
|  | 		"annotationKey": "annotationValue", | ||||||
|  | 	} | ||||||
|  | 	item := onepassword.Item{} | ||||||
|  |  | ||||||
|  | 	item.Fields = []*onepassword.ItemField{ | ||||||
|  | 		{ | ||||||
|  | 			Label: "label w%th invalid ch!rs-", | ||||||
|  | 			Value: "value1", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			Label: strings.Repeat("x", kubeValidate.DNS1123SubdomainMaxLength+1), | ||||||
|  | 			Value: "name exceeds max length", | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	kubeSecret := BuildKubernetesSecretFromOnePasswordItem(name, namespace, annotations, item) | ||||||
|  |  | ||||||
|  | 	// Assert Secret's meta.name was fixed | ||||||
|  | 	if kubeSecret.Name != expectedName { | ||||||
|  | 		t.Errorf("Expected name value: %v but got: %v", name, kubeSecret.Name) | ||||||
|  | 	} | ||||||
|  | 	if kubeSecret.Namespace != namespace { | ||||||
|  | 		t.Errorf("Expected namespace value: %v but got: %v", namespace, kubeSecret.Namespace) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// assert labels were fixed for each data key | ||||||
|  | 	for key := range kubeSecret.Data { | ||||||
|  | 		if !validLabel(key) { | ||||||
|  | 			t.Errorf("Expected valid kubernetes label, got %s", key) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| func compareAnnotationsToItem(annotations map[string]string, item onepassword.Item, t *testing.T) { | func compareAnnotationsToItem(annotations map[string]string, item onepassword.Item, t *testing.T) { | ||||||
| 	actualVaultId, actualItemId, err := ParseVaultIdAndItemIdFromPath(annotations[ItemPathAnnotation]) | 	actualVaultId, actualItemId, err := ParseVaultIdAndItemIdFromPath(annotations[ItemPathAnnotation]) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @@ -164,3 +203,10 @@ func ParseVaultIdAndItemIdFromPath(path string) (string, string, error) { | |||||||
| 	} | 	} | ||||||
| 	return "", "", fmt.Errorf("%q is not an acceptable path for One Password item. Must be of the format: `vaults/{vault_id}/items/{item_id}`", path) | 	return "", "", fmt.Errorf("%q is not an acceptable path for One Password item. Must be of the format: `vaults/{vault_id}/items/{item_id}`", path) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func validLabel(v string) bool { | ||||||
|  | 	if err := kubeValidate.IsDNS1123Subdomain(v); len(err) > 0 { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 	return true | ||||||
|  | } | ||||||
|   | |||||||
| @@ -2,6 +2,7 @@ package onepassword | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
|  | 	v1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||||
| 	"os" | 	"os" | ||||||
|  |  | ||||||
| 	appsv1 "k8s.io/api/apps/v1" | 	appsv1 "k8s.io/api/apps/v1" | ||||||
| @@ -17,13 +18,13 @@ var logConnectSetup = logf.Log.WithName("ConnectSetup") | |||||||
| var deploymentPath = "deploy/connect/deployment.yaml" | var deploymentPath = "deploy/connect/deployment.yaml" | ||||||
| var servicePath = "deploy/connect/service.yaml" | var servicePath = "deploy/connect/service.yaml" | ||||||
|  |  | ||||||
| func SetupConnect(kubeClient client.Client) error { | func SetupConnect(kubeClient client.Client, deploymentNamespace string) error { | ||||||
| 	err := setupService(kubeClient, servicePath) | 	err := setupService(kubeClient, servicePath, deploymentNamespace) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	err = setupDeployment(kubeClient, deploymentPath) | 	err = setupDeployment(kubeClient, deploymentPath, deploymentNamespace) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| @@ -31,22 +32,22 @@ func SetupConnect(kubeClient client.Client) error { | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func setupDeployment(kubeClient client.Client, deploymentPath string) error { | func setupDeployment(kubeClient client.Client, deploymentPath string, deploymentNamespace string) error { | ||||||
| 	existingDeployment := &appsv1.Deployment{} | 	existingDeployment := &appsv1.Deployment{} | ||||||
|  |  | ||||||
| 	// check if deployment has already been created | 	// check if deployment has already been created | ||||||
| 	err := kubeClient.Get(context.Background(), types.NamespacedName{Name: "onepassword-connect", Namespace: "default"}, existingDeployment) | 	err := kubeClient.Get(context.Background(), types.NamespacedName{Name: "onepassword-connect", Namespace: deploymentNamespace}, existingDeployment) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		if errors.IsNotFound(err) { | 		if errors.IsNotFound(err) { | ||||||
| 			logConnectSetup.Info("No existing Connect deployment found. Creating Deployment") | 			logConnectSetup.Info("No existing Connect deployment found. Creating Deployment") | ||||||
| 			return createDeployment(kubeClient, deploymentPath) | 			return createDeployment(kubeClient, deploymentPath, deploymentNamespace) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	return err | 	return err | ||||||
| } | } | ||||||
|  |  | ||||||
| func createDeployment(kubeClient client.Client, deploymentPath string) error { | func createDeployment(kubeClient client.Client, deploymentPath string, deploymentNamespace string) error { | ||||||
| 	deployment, err := getDeploymentToCreate(deploymentPath) | 	deployment, err := getDeploymentToCreate(deploymentPath, deploymentNamespace) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| @@ -59,12 +60,16 @@ func createDeployment(kubeClient client.Client, deploymentPath string) error { | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func getDeploymentToCreate(deploymentPath string) (*appsv1.Deployment, error) { | func getDeploymentToCreate(deploymentPath string, deploymentNamespace string) (*appsv1.Deployment, error) { | ||||||
| 	f, err := os.Open(deploymentPath) | 	f, err := os.Open(deploymentPath) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 	deployment := &appsv1.Deployment{} | 	deployment := &appsv1.Deployment{ | ||||||
|  | 		ObjectMeta: v1.ObjectMeta{ | ||||||
|  | 			Namespace: deploymentNamespace, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	err = yaml.NewYAMLOrJSONDecoder(f, 4096).Decode(deployment) | 	err = yaml.NewYAMLOrJSONDecoder(f, 4096).Decode(deployment) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @@ -73,26 +78,30 @@ func getDeploymentToCreate(deploymentPath string) (*appsv1.Deployment, error) { | |||||||
| 	return deployment, nil | 	return deployment, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func setupService(kubeClient client.Client, servicePath string) error { | func setupService(kubeClient client.Client, servicePath string, deploymentNamespace string) error { | ||||||
| 	existingService := &corev1.Service{} | 	existingService := &corev1.Service{} | ||||||
|  |  | ||||||
| 	//check if service has already been created | 	//check if service has already been created | ||||||
| 	err := kubeClient.Get(context.Background(), types.NamespacedName{Name: "onepassword-connect", Namespace: "default"}, existingService) | 	err := kubeClient.Get(context.Background(), types.NamespacedName{Name: "onepassword-connect", Namespace: deploymentNamespace}, existingService) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		if errors.IsNotFound(err) { | 		if errors.IsNotFound(err) { | ||||||
| 			logConnectSetup.Info("No existing Connect service found. Creating Service") | 			logConnectSetup.Info("No existing Connect service found. Creating Service") | ||||||
| 			return createService(kubeClient, servicePath) | 			return createService(kubeClient, servicePath, deploymentNamespace) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	return err | 	return err | ||||||
| } | } | ||||||
|  |  | ||||||
| func createService(kubeClient client.Client, servicePath string) error { | func createService(kubeClient client.Client, servicePath string, deploymentNamespace string) error { | ||||||
| 	f, err := os.Open(servicePath) | 	f, err := os.Open(servicePath) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	service := &corev1.Service{} | 	service := &corev1.Service{ | ||||||
|  | 		ObjectMeta: v1.ObjectMeta{ | ||||||
|  | 			Namespace: deploymentNamespace, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	err = yaml.NewYAMLOrJSONDecoder(f, 4096).Decode(service) | 	err = yaml.NewYAMLOrJSONDecoder(f, 4096).Decode(service) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
|   | |||||||
| @@ -25,7 +25,7 @@ func TestServiceSetup(t *testing.T) { | |||||||
| 	// Create a fake client to mock API calls. | 	// Create a fake client to mock API calls. | ||||||
| 	client := fake.NewFakeClientWithScheme(s, objs...) | 	client := fake.NewFakeClientWithScheme(s, objs...) | ||||||
|  |  | ||||||
| 	err := setupService(client, "../../deploy/connect/service.yaml") | 	err := setupService(client, "../../deploy/connect/service.yaml", defaultNamespacedName.Namespace) | ||||||
|  |  | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Errorf("Error Setting Up Connect: %v", err) | 		t.Errorf("Error Setting Up Connect: %v", err) | ||||||
| @@ -50,7 +50,7 @@ func TestDeploymentSetup(t *testing.T) { | |||||||
| 	// Create a fake client to mock API calls. | 	// Create a fake client to mock API calls. | ||||||
| 	client := fake.NewFakeClientWithScheme(s, objs...) | 	client := fake.NewFakeClientWithScheme(s, objs...) | ||||||
|  |  | ||||||
| 	err := setupDeployment(client, "../../deploy/connect/deployment.yaml") | 	err := setupDeployment(client, "../../deploy/connect/deployment.yaml", defaultNamespacedName.Namespace) | ||||||
|  |  | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Errorf("Error Setting Up Connect: %v", err) | 		t.Errorf("Error Setting Up Connect: %v", err) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user