mirror of
https://github.com/1Password/onepassword-operator.git
synced 2025-10-22 07:28:06 +00:00
Compare commits
124 Commits
v0.0.2
...
release/v1
Author | SHA1 | Date | |
---|---|---|---|
![]() |
a37bddbfd9 | ||
![]() |
bd9922f635 | ||
![]() |
8fa4413880 | ||
![]() |
62e55a3f19 | ||
![]() |
d6f7b80c40 | ||
![]() |
a903f9b1af | ||
![]() |
5cddc9d8a9 | ||
![]() |
7e1b94fae7 | ||
![]() |
6953a89c89 | ||
![]() |
0d9e07f543 | ||
![]() |
098d504d2a | ||
![]() |
b68d9a5d79 | ||
![]() |
befcaae457 | ||
![]() |
b24aa48bd6 | ||
![]() |
b1e251dee6 | ||
![]() |
a34c6e8b38 | ||
![]() |
b16960057a | ||
![]() |
285496dc7e | ||
![]() |
f38cf7e1c2 | ||
![]() |
bb7a0c8ca9 | ||
![]() |
302653832e | ||
![]() |
a1bcfdfdcb | ||
![]() |
c0f1632638 | ||
![]() |
c46065fa7a | ||
![]() |
5d229c42d5 | ||
![]() |
c7235b4f09 | ||
![]() |
5183fc129a | ||
![]() |
7d619165b2 | ||
![]() |
0363ae1e4e | ||
![]() |
d9e003bdb7 | ||
![]() |
b25f943b3a | ||
![]() |
5fab662424 | ||
![]() |
d807e92c36 | ||
![]() |
244771717c | ||
![]() |
7aeb36e383 | ||
![]() |
5c2f840623 | ||
![]() |
670040477e | ||
![]() |
a45a310611 | ||
![]() |
d80e8dd799 | ||
![]() |
88728909ff | ||
![]() |
e365ebfdfa | ||
![]() |
2c4b4df01a | ||
![]() |
49d984c6f2 | ||
![]() |
72cad7284c | ||
![]() |
0193a98681 | ||
![]() |
f241d7423d | ||
![]() |
6043e0da0b | ||
![]() |
753cc5e9a3 | ||
![]() |
8cfe98073e | ||
![]() |
c0037526b0 | ||
![]() |
96b42e7c52 | ||
![]() |
579b5848da | ||
![]() |
dff934cbc3 | ||
![]() |
2096f4440f | ||
![]() |
b3fc707337 | ||
![]() |
313cd1169b | ||
![]() |
b50d864b50 | ||
![]() |
1643385d9b | ||
![]() |
9441214733 | ||
![]() |
7e4e988813 | ||
![]() |
fb1262f1bd | ||
![]() |
68f084080e | ||
![]() |
a428fe7462 | ||
![]() |
ea2d1f8a09 | ||
![]() |
bd96d50a9b | ||
![]() |
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 | ||
![]() |
b574e394ad | ||
![]() |
2b92214cf5 | ||
![]() |
5422e37d77 | ||
![]() |
ac05846062 | ||
![]() |
dcc37519ef | ||
![]() |
73b089df79 | ||
![]() |
97650573fd | ||
![]() |
42e348de91 | ||
![]() |
9946ce7ba6 | ||
![]() |
71ccfc6235 | ||
![]() |
6cb8b87560 | ||
![]() |
62ca0c25fd | ||
![]() |
990ac86297 | ||
![]() |
077142d9f2 | ||
![]() |
d8a969265c | ||
![]() |
d98f9172a0 | ||
![]() |
8635be0cab | ||
![]() |
0824aa0837 | ||
![]() |
10b7db3057 | ||
![]() |
e2fc9e228e | ||
![]() |
d0eafd93ab | ||
![]() |
6aea2ad31c | ||
![]() |
6c98b16766 | ||
![]() |
7e3ab368e2 | ||
![]() |
b60fa8a444 | ||
![]() |
854112caeb | ||
![]() |
eebb90e43b | ||
![]() |
9f708729a8 | ||
![]() |
143b6f9403 | ||
![]() |
76ee62519e |
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 -->
|
21
.github/workflows/build.yml
vendored
Normal file
21
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
name: Build and Test
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ^1.15
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Build
|
||||
run: go build -v ./...
|
||||
|
||||
- name: Test
|
||||
run: go test -v ./... -cover
|
85
.github/workflows/release-pr.yml
vendored
Normal file
85
.github/workflows/release-pr.yml
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
on:
|
||||
create:
|
||||
branches:
|
||||
|
||||
name: Open Release PR for review
|
||||
|
||||
jobs:
|
||||
# This job is necessary because GitHub does not (yet) support
|
||||
# filtering `create` triggers by branch name.
|
||||
# See: https://github.community/t/trigger-job-on-branch-created/16878/5
|
||||
should_create_pr:
|
||||
name: Check if PR for branch already exists
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
result: ${{ steps.is_release_branch_without_pr.outputs.result }}
|
||||
steps:
|
||||
- id: is_release_branch_without_pr
|
||||
name: Find matching PR
|
||||
uses: actions/github-script@v3
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
// Search for an existing PR with head & base
|
||||
// that match the created branch
|
||||
|
||||
const [releaseBranchName] = context.ref.match("release/v[0-9]+\.[0-9]+\.[0-9]+") || []
|
||||
|
||||
if(!releaseBranchName) { return false }
|
||||
|
||||
const {data: prs} = await github.pulls.list({
|
||||
...context.repo,
|
||||
state: 'open',
|
||||
head: `1Password:${releaseBranchName}`,
|
||||
base: context.payload.master_branch
|
||||
})
|
||||
|
||||
return prs.length === 0
|
||||
|
||||
create_pr:
|
||||
needs: should_create_pr
|
||||
if: needs.should_create_pr.outputs.result == 'true'
|
||||
name: Create Release Pull Request
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Parse release version
|
||||
id: get_version
|
||||
run: echo "::set-output name=version::$(echo $GITHUB_REF | sed 's|^refs/heads/release/v?*||g')"
|
||||
|
||||
- name: Prepare Pull Request
|
||||
id: prep_pr
|
||||
run: |
|
||||
CHANGELOG_PATH=$(printf "%s/CHANGELOG.md" "${GITHUB_WORKSPACE}")
|
||||
|
||||
LOG_ENTRY=$(awk '/START\/v[0-9]+\.[0-9]+\.[0-9]+*/{f=1; next} /---/{if (f == 1) exit} f' "${CHANGELOG_PATH}")
|
||||
export PR_BODY=$(cat <<EOF
|
||||
This is an automated PR for a new release.
|
||||
|
||||
Please check the following before approving:
|
||||
- [ ] Changelog is accurate. The documented changes for this release are printed below.
|
||||
- [ ] Any files referencing a version number. Confirm it matches the version number in the branch name.
|
||||
---
|
||||
## Release Changelog Preview
|
||||
${LOG_ENTRY}
|
||||
EOF
|
||||
)
|
||||
|
||||
# Sanitizes multiline strings for action outputs (https://medium.com/agorapulse-stories/23f56447d209)
|
||||
PR_BODY="${PR_BODY//'%'/'%25'}"
|
||||
PR_BODY="${PR_BODY//$'\n'/'%0A'}"
|
||||
PR_BODY="${PR_BODY//$'\r'/'%0D'}"
|
||||
echo "::set-output name=pr_body::$(echo "$PR_BODY")"
|
||||
|
||||
- name: Create Pull Request via API
|
||||
id: post_pr
|
||||
uses: octokit/request-action@v2.x
|
||||
with:
|
||||
route: POST /repos/${{ github.repository }}/pulls
|
||||
title: ${{ format('Prepare Release - v{0}', steps.get_version.outputs.version) }}
|
||||
head: ${{ github.ref }}
|
||||
base: ${{ github.event.master_branch }}
|
||||
body: ${{ toJson(steps.prep_pr.outputs.pr_body) }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
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 }}
|
96
CHANGELOG.md
Normal file
96
CHANGELOG.md
Normal file
@@ -0,0 +1,96 @@
|
||||
[//]: # (START/LATEST)
|
||||
# Latest
|
||||
|
||||
## Features
|
||||
* A user-friendly description of a new feature. {issue-number}
|
||||
|
||||
## Fixes
|
||||
* A user-friendly description of a fix. {issue-number}
|
||||
|
||||
## Security
|
||||
* A user-friendly description of a security fix. {issue-number}
|
||||
|
||||
---
|
||||
|
||||
[//]: # (START/v1.3.0)
|
||||
# v1.3.0
|
||||
|
||||
## Features
|
||||
* Added support for loading secrets from files stored in 1Password. {#47}
|
||||
|
||||
---
|
||||
|
||||
[//]: # (START/v1.2.0)
|
||||
# v1.2.0
|
||||
|
||||
## Features
|
||||
* Support secrets provisioned through FromEnv. {#74}
|
||||
* Support configuration of Kubernetes Secret type. {#87}
|
||||
* Improved logging. (#72)
|
||||
---
|
||||
|
||||
[//]: # (START/v1.1.0)
|
||||
# v1.1.0
|
||||
|
||||
## Fixes
|
||||
* Fix normalization for keys in a Secret's `data` section to allow upper- and lower-case alphanumeric characters. {#66}
|
||||
|
||||
---
|
||||
|
||||
[//]: # (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)
|
||||
# v1.0.0
|
||||
|
||||
## Features:
|
||||
* Option to automatically deploy 1Password Connect via the operator
|
||||
* Ignore restart annotation when looking for 1Password annotations
|
||||
* Release Automation
|
||||
* Upgrading apiextensions.k8s.io/v1beta apiversion from the operator custom resource
|
||||
* Adding configuration for auto rolling restart on deployments
|
||||
* Configure Auto Restarts for a OnePasswordItem Custom Resource
|
||||
* Update Connect Dependencies to latest
|
||||
* Add Github action for building and testing operator
|
||||
## Fixes:
|
||||
* Fix spec field example for OnePasswordItem in readme
|
||||
* Casing of annotations are now consistent
|
||||
|
||||
---
|
||||
|
||||
[//]: # (START/v0.0.2)
|
||||
# v0.0.2
|
||||
|
||||
## Features:
|
||||
* Items can now be accessed by either `vaults/<vault_id>/items/<item_id>` or `vaults/<vault_title>/items/<item_title>`
|
||||
|
||||
---
|
||||
|
||||
[//]: # (START/v0.0.1)
|
||||
|
||||
# v0.0.1
|
||||
|
||||
Initial 1Password Operator release
|
||||
|
||||
## Features
|
||||
* watches for deployment creations with `onepassword` annotations and creates an associated kubernetes secret
|
||||
* watches for `onepasswordsecret` crd creations and creates an associated kubernetes secrets
|
||||
* watches for changes to 1Password secrets associated with kubernetes secrets and updates the kubernetes secret with changes
|
||||
* restart pods when secret has been updated
|
||||
* cleanup of kubernetes secrets when deployment or `onepasswordsecret` is deleted
|
||||
|
||||
---
|
11
Dockerfile
11
Dockerfile
@@ -12,7 +12,13 @@ COPY pkg/ pkg/
|
||||
COPY version/ version/
|
||||
COPY vendor/ vendor/
|
||||
# Build
|
||||
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go build -mod vendor -a -o manager main.go
|
||||
ARG operator_version=dev
|
||||
RUN CGO_ENABLED=0 \
|
||||
GO111MODULE=on \
|
||||
go build \
|
||||
-ldflags "-X \"github.com/1Password/onepassword-operator/version.Version=$operator_version\"" \
|
||||
-mod vendor \
|
||||
-a -o manager main.go
|
||||
|
||||
# Use distroless as minimal base image to package the manager binary
|
||||
# Refer to https://github.com/GoogleContainerTools/distroless for more details
|
||||
@@ -20,5 +26,6 @@ FROM gcr.io/distroless/static:nonroot
|
||||
WORKDIR /
|
||||
COPY --from=builder /workspace/manager .
|
||||
USER nonroot:nonroot
|
||||
COPY deploy/connect/ deploy/connect/
|
||||
|
||||
ENTRYPOINT ["/manager"]
|
||||
ENTRYPOINT ["/manager"]
|
||||
|
68
Makefile
Normal file
68
Makefile
Normal file
@@ -0,0 +1,68 @@
|
||||
export MAIN_BRANCH ?= main
|
||||
|
||||
.DEFAULT_GOAL := help
|
||||
.PHONY: test build build/binary build/local clean test/coverage release/prepare release/tag .check_bump_type .check_git_clean help
|
||||
|
||||
GIT_BRANCH := $(shell git symbolic-ref --short HEAD)
|
||||
WORKTREE_CLEAN := $(shell git status --porcelain 1>/dev/null 2>&1; echo $$?)
|
||||
SCRIPTS_DIR := $(CURDIR)/scripts
|
||||
|
||||
versionFile = $(CURDIR)/.VERSION
|
||||
curVersion := $(shell cat $(versionFile) | sed 's/^v//')
|
||||
|
||||
OPERATOR_NAME := onepassword-connect-operator
|
||||
DOCKER_IMG_TAG ?= $(OPERATOR_NAME):v$(curVersion)
|
||||
|
||||
test: ## Run test suite
|
||||
go test ./...
|
||||
|
||||
test/coverage: ## Run test suite with coverage report
|
||||
go test -v ./... -cover
|
||||
|
||||
build: ## Build operator Docker image
|
||||
@docker build -f Dockerfile --build-arg operator_version=$(curVersion) -t $(DOCKER_IMG_TAG) .
|
||||
@echo "Successfully built and tagged image."
|
||||
@echo "Tag: $(DOCKER_IMG_TAG)"
|
||||
|
||||
build/local: ## Build local version of the operator Docker image
|
||||
@docker build -f Dockerfile -t local/$(DOCKER_IMG_TAG) .
|
||||
|
||||
build/binary: clean ## Build operator binary
|
||||
@mkdir -p dist
|
||||
@go build -mod vendor -a -o manager ./cmd/manager/main.go
|
||||
@mv manager ./dist
|
||||
|
||||
clean:
|
||||
rm -rf ./dist
|
||||
|
||||
help: ## Prints this help message
|
||||
@grep -E '^[\/a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
|
||||
|
||||
|
||||
## Release functions =====================
|
||||
|
||||
release/prepare: .check_git_clean ## Updates changelog and creates release branch (call with 'release/prepare version=<new_version_number>')
|
||||
|
||||
@test $(version) || (echo "[ERROR] version argument not set."; exit 1)
|
||||
@git fetch --quiet origin $(MAIN_BRANCH)
|
||||
|
||||
@echo $(version) | tr -d '\n' | tee $(versionFile) &>/dev/null
|
||||
|
||||
@NEW_VERSION=$(version) $(SCRIPTS_DIR)/prepare-release.sh
|
||||
|
||||
release/tag: .check_git_clean ## Creates git tag
|
||||
@git pull --ff-only
|
||||
@echo "Applying tag 'v$(curVersion)' to HEAD..."
|
||||
@git tag --sign "v$(curVersion)" -m "Release v$(curVersion)"
|
||||
@echo "[OK] Success!"
|
||||
@echo "Remember to call 'git push --tags' to persist the tag."
|
||||
|
||||
## Helper functions =====================
|
||||
|
||||
.check_git_clean:
|
||||
ifneq ($(GIT_BRANCH), $(MAIN_BRANCH))
|
||||
@echo "[ERROR] Please checkout default branch '$(MAIN_BRANCH)' and re-run this command."; exit 1;
|
||||
endif
|
||||
ifneq ($(WORKTREE_CLEAN), 0)
|
||||
@echo "[ERROR] Uncommitted changes found in worktree. Address them and try again."; exit 1;
|
||||
endif
|
175
README.md
175
README.md
@@ -4,7 +4,7 @@ The 1Password Connect Kubernetes Operator provides the ability to integrate Kube
|
||||
|
||||
The 1Password Connect Kubernetes Operator also allows for Kubernetes Secrets to be composed from a 1Password Item through annotation of an Item Path on a deployment.
|
||||
|
||||
The 1Password Connect Kubernetes Operator will continually check for updates from 1Password for any Kubernetes Secret that it has generated. If a Kubernetes Secret is updated, any Deployment using that secret will be automatically restarted.
|
||||
The 1Password Connect Kubernetes Operator will continually check for updates from 1Password for any Kubernetes Secret that it has generated. If a Kubernetes Secret is updated, any Deployment using that secret can be automatically restarted.
|
||||
|
||||
## Setup
|
||||
|
||||
@@ -13,53 +13,81 @@ Prerequisites:
|
||||
- [1Password Command Line Tool Installed](https://1password.com/downloads/command-line/)
|
||||
- [kubectl installed](https://kubernetes.io/docs/tasks/tools/install-kubectl/)
|
||||
- [docker installed](https://docs.docker.com/get-docker/)
|
||||
- [1Password Connect has been setup with an API token issued to be used with the operator.](https://support.b5dev.com/cs/connect)
|
||||
- [1Password Connect deployed to Kubernetes](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.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
|
||||
|
||||
|
||||
#### Deploy with Helm
|
||||
The 1Password Connect Helm Chart helps to simplify the deployment of 1Password Connect and the 1Password Connect Kubernetes Operator to Kubernetes.
|
||||
|
||||
[The 1Password Connect Helm Chart can be found here.](https://github.com/1Password/connect-helm-charts)
|
||||
|
||||
#### Deploy using the Connect Operator
|
||||
If 1Password Connect is already running, you can skip this step. This guide will provide a quickstart option for deploying a default configuration of 1Password Connect via starting the deploying the 1Password Connect Operator, however it is recommended that you instead deploy your own manifest file if customization of the 1Password Connect deployment is desired.
|
||||
|
||||
Encode the 1password-credentials.json file you generated in the prerequisite steps and save it to a file named op-session:
|
||||
|
||||
```bash
|
||||
cat 1password-credentials.json | base64 | \
|
||||
tr '/+' '_-' | tr -d '=' | tr -d '\n' > op-session
|
||||
```
|
||||
|
||||
Create a Kubernetes secret from the op-session file:
|
||||
```bash
|
||||
kubectl create secret generic op-credentials --from-file=op-session
|
||||
```
|
||||
|
||||
Add the following environment variable to the onepassword-connect-operator container in `deploy/operator.yaml`:
|
||||
```yaml
|
||||
- name: MANAGE_CONNECT
|
||||
value: "true"
|
||||
```
|
||||
Adding this environment variable will have the operator automatically deploy a default configuration of 1Password Connect to the `default` namespace.
|
||||
### Kubernetes Operator Deployment
|
||||
|
||||
**Create Kubernetes Secret for OP_CONNECT_TOKEN**
|
||||
|
||||
"Create a Connect token for the operator and save it as a Kubernetes Secret:
|
||||
|
||||
```bash
|
||||
# where <OP_CONNECT_TOKEN> is the 1Password Connect API token
|
||||
$ kubectl create secret generic onepassword-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:
|
||||
```bash
|
||||
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/secrets-automation/#appendix-issue-additional-access-tokens)
|
||||
|
||||
**Set Permissions For Operator**
|
||||
|
||||
We must create a service account, role, and role binding and Kubernetes. Examples can be found in the `/deploy` folder.
|
||||
|
||||
```bash
|
||||
$ kubectl apply -f deploy/permissions.yaml
|
||||
kubectl apply -f deploy/permissions.yaml
|
||||
```
|
||||
|
||||
**Create Custom One Password Secret Resource**
|
||||
|
||||
```bash
|
||||
$ kubectl apply -f deploy/crds/onepassword.com_onepassworditems_crd.yaml
|
||||
kubectl apply -f deploy/crds/onepassword.com_onepassworditems_crd.yaml
|
||||
```
|
||||
|
||||
**Deploying the Operator**
|
||||
|
||||
An example Deployment yaml can be found at `/deploy/operator.yaml`.
|
||||
An sample Deployment yaml can be found at `/deploy/operator.yaml`.
|
||||
|
||||
```yaml
|
||||
containers:
|
||||
- name: onepassword-operator
|
||||
image: 1password/onepassword-operator
|
||||
```
|
||||
|
||||
and update the image pull policy to `Always`
|
||||
To further configure the 1Password Kubernetes Operator the Following Environment variables can be set in the operator yaml:
|
||||
|
||||
```yaml
|
||||
imagePullPolicy: Always
|
||||
```
|
||||
|
||||
To further configure the 1Password Kubernetes Operator the Following Environment variables can be set in the deployment 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.
|
||||
- **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.
|
||||
- **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.
|
||||
|
||||
Apply the deployment file:
|
||||
|
||||
@@ -73,23 +101,23 @@ To create a Kubernetes Secret from a 1Password item, create a yaml file with the
|
||||
|
||||
```yaml
|
||||
apiVersion: onepassword.com/v1
|
||||
kind: OnePasswordItem # {insert_new_name}
|
||||
kind: OnePasswordItem
|
||||
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:
|
||||
item-path: "vaults/{vault_id_or_title}/items/{item_id_or_title}"
|
||||
itemPath: "vaults/<vault_id_or_title>/items/<item_id_or_title>"
|
||||
```
|
||||
|
||||
Deploy the OnePasswordItem to Kubernetes:
|
||||
|
||||
```bash
|
||||
$ kubectl apply -f {your_item}.yaml
|
||||
kubectl apply -f <your_item>.yaml
|
||||
```
|
||||
|
||||
To test that the Kubernetes Secret check that the following command returns a secret:
|
||||
|
||||
```bash
|
||||
$ kubectl get secret {secret_name}
|
||||
kubectl get secret <secret_name>
|
||||
```
|
||||
|
||||
Note: Deleting the `OnePasswordItem` that you've created will automatically delete the created Kubernetes Secret.
|
||||
@@ -102,29 +130,104 @@ kind: Deployment
|
||||
metadata:
|
||||
name: deployment-example
|
||||
annotations:
|
||||
onepasswordoperator/item-path: "vaults/{vault_id_or_title}/items/{item_id_or_title}"
|
||||
onepasswordoperator/item-name: "{secret_name}"
|
||||
operator.1password.io/item-path: "vaults/<vault_id_or_title>/items/<item_id_or_title>"
|
||||
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.
|
||||
|
||||
Note: Deleting the Deployment that you've created will automatically delete the created Kubernetes Secret only if the deployment is still annotated with `onepasswordoperator./item-path` and `onepasswordoperator/item-name` and no other deployment is using the secret.
|
||||
The contents of the Kubernetes secret will be key-value pairs in which the keys are the fields of the 1Password item and the values are the corresponding values stored in 1Password.
|
||||
In case of fields that store files, the file's contents will be used as the value.
|
||||
|
||||
If a 1Password Item that is linked to a Kubernetes Secret is updated within the `POLLING_INTERVAL` the associated Kubernetes Secret will be updated. Furthermore, any deployments using that secret will be given a rolling restart.
|
||||
Within an item, if both a field storing a file and a field of another type have the same name, the file field will be ignored and the other field will take precedence.
|
||||
|
||||
Note: Deleting the Deployment that you've created will automatically delete the created Kubernetes Secret only if the deployment is still annotated with `operator.1password.io/item-path` and `operator.1password.io/item-name` and no other deployment is using the secret.
|
||||
|
||||
If a 1Password Item that is linked to a Kubernetes Secret is updated within the POLLING_INTERVAL the associated Kubernetes Secret will be updated. However, if you do not want a specific secret to be updated you can add the tag `operator.1password.io:ignore-secret` to the item stored in 1Password. While this tag is in place, any updates made to an item will not trigger an update to the associated secret in Kubernetes.
|
||||
|
||||
---
|
||||
**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.
|
||||
|
||||
---
|
||||
|
||||
### Configuring Automatic Rolling Restarts of Deployments
|
||||
|
||||
If a 1Password Item that is linked to a Kubernetes Secret is updated, any deployments configured to `auto-restart` AND are using that secret will be given a rolling restart the next time 1Password Connect is polled for updates.
|
||||
|
||||
There are many levels of granularity on which to configure auto restarts on deployments: at the operator level, per-namespace, or per-deployment.
|
||||
|
||||
**On the operator**: This method allows for managing auto restarts on all deployments within the namespaces watched by operator. Auto restarts can be enabled by setting the environemnt variable `AUTO_RESTART` to true. If the value is not set, the operator will default this value to false.
|
||||
|
||||
**Per Namespace**: This method allows for managing auto restarts on all deployments within a namespace. Auto restarts can by managed by setting the annotation `operator.1password.io/auto-restart` to either `true` or `false` on the desired namespace. An example of this is shown below:
|
||||
```yaml
|
||||
# enabled auto restarts for all deployments within a namespace unless overwritten within a deployment
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: "example-namespace"
|
||||
annotations:
|
||||
operator.1password.io/auto-restart: "true"
|
||||
```
|
||||
If the value is not set, the auto restart settings on the operator will be used. This value can be overwritten by deployment.
|
||||
|
||||
**Per Deployment**
|
||||
This method allows for managing auto restarts on a given deployment. Auto restarts can by managed by setting the annotation `operator.1password.io/auto-restart` to either `true` or `false` on the desired deployment. An example of this is shown below:
|
||||
```yaml
|
||||
# enabled auto restarts for the deployment
|
||||
apiVersion: v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: "example-deployment"
|
||||
annotations:
|
||||
operator.1password.io/auto-restart: "true"
|
||||
```
|
||||
If the value is not set, the auto restart settings on the namespace will be used.
|
||||
|
||||
**Per OnePasswordItem Custom Resource**
|
||||
This method allows for managing auto restarts on a given OnePasswordItem custom resource. Auto restarts can by managed by setting the annotation `operator.1password.io/auto_restart` to either `true` or `false` on the desired OnePasswordItem. An example of this is shown below:
|
||||
```yaml
|
||||
# enabled auto restarts for the OnePasswordItem
|
||||
apiVersion: onepassword.com/v1
|
||||
kind: OnePasswordItem
|
||||
metadata:
|
||||
name: example
|
||||
annotations:
|
||||
operator.1password.io/auto-restart: "true"
|
||||
```
|
||||
If the value is not set, the auto restart settings on the deployment will be used.
|
||||
|
||||
## Development
|
||||
|
||||
### Creating a Docker image
|
||||
|
||||
To create a local version of the Docker image for testing, use the following `Makefile` target:
|
||||
```shell
|
||||
make build/local
|
||||
```
|
||||
|
||||
### Building the Operator binary
|
||||
```shell
|
||||
make build/binary
|
||||
```
|
||||
|
||||
The binary will be placed inside a `dist` folder within this repository.
|
||||
|
||||
### Running Tests
|
||||
|
||||
```bash
|
||||
$ go test -v ./... -cover
|
||||
```shell
|
||||
make test
|
||||
```
|
||||
|
||||
With coverage:
|
||||
```shell
|
||||
make test/coverage
|
||||
```
|
||||
|
||||
## Security
|
||||
@@ -133,4 +236,4 @@ $ go test -v ./... -cover
|
||||
|
||||
Please file requests via [**BugCrowd**](https://bugcrowd.com/agilebits).
|
||||
|
||||
For information about security practices, please visit our [Security homepage](https://bugcrowd.com/agilebits).
|
||||
For information about security practices, please visit our [Security homepage](https://bugcrowd.com/agilebits).
|
||||
|
@@ -41,6 +41,8 @@ import (
|
||||
)
|
||||
|
||||
const envPollingIntervalVariable = "POLLING_INTERVAL"
|
||||
const manageConnect = "MANAGE_CONNECT"
|
||||
const restartDeploymentsEnvVariable = "AUTO_RESTART"
|
||||
const defaultPollingInterval = 600
|
||||
|
||||
// Change below variables to serve metrics on different host or port.
|
||||
@@ -81,9 +83,11 @@ func main() {
|
||||
|
||||
printVersion()
|
||||
|
||||
namespace, err := k8sutil.GetWatchNamespace()
|
||||
namespace := os.Getenv(k8sutil.WatchNamespaceEnvVar)
|
||||
|
||||
deploymentNamespace, err := k8sutil.GetOperatorNamespace()
|
||||
if err != nil {
|
||||
log.Error(err, "Failed to get watch namespace")
|
||||
log.Error(err, "Failed to get namespace")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
@@ -131,6 +135,27 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
//Setup 1PasswordConnect
|
||||
if shouldManageConnect() {
|
||||
log.Info("Automated Connect Management Enabled")
|
||||
go func() {
|
||||
connectStarted := false
|
||||
for connectStarted == false {
|
||||
err := op.SetupConnect(mgr.GetClient(), deploymentNamespace)
|
||||
// Cache Not Started is an acceptable error. Retry until cache is started.
|
||||
if err != nil && !errors.Is(err, &cache.ErrCacheNotStarted{}) {
|
||||
log.Error(err, "")
|
||||
os.Exit(1)
|
||||
}
|
||||
if err == nil {
|
||||
connectStarted = true
|
||||
}
|
||||
}
|
||||
}()
|
||||
} else {
|
||||
log.Info("Automated Connect Management Disabled")
|
||||
}
|
||||
|
||||
// Setup One Password Client
|
||||
opConnectClient, err := connect.NewClientFromEnvironment()
|
||||
|
||||
@@ -143,7 +168,7 @@ func main() {
|
||||
addMetrics(ctx, cfg)
|
||||
|
||||
// Setup update secrets task
|
||||
updatedSecretsPoller := op.NewManager(mgr.GetClient(), opConnectClient)
|
||||
updatedSecretsPoller := op.NewManager(mgr.GetClient(), opConnectClient, shouldAutoRestartDeployments())
|
||||
done := make(chan bool)
|
||||
ticker := time.NewTicker(getPollingIntervalForUpdatingSecrets())
|
||||
go func() {
|
||||
@@ -153,7 +178,10 @@ func main() {
|
||||
ticker.Stop()
|
||||
return
|
||||
case <-ticker.C:
|
||||
updatedSecretsPoller.UpdateKubernetesSecretsTask()
|
||||
err := updatedSecretsPoller.UpdateKubernetesSecretsTask()
|
||||
if err != nil {
|
||||
log.Error(err, "error running update kubernetes secret task")
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
@@ -249,3 +277,29 @@ func getPollingIntervalForUpdatingSecrets() time.Duration {
|
||||
log.Info(fmt.Sprintf("Using default polling interval of %v seconds", defaultPollingInterval))
|
||||
return time.Duration(defaultPollingInterval) * time.Second
|
||||
}
|
||||
|
||||
func shouldManageConnect() bool {
|
||||
shouldManageConnect, found := os.LookupEnv(manageConnect)
|
||||
if found {
|
||||
shouldManageConnectBool, err := strconv.ParseBool(strings.ToLower(shouldManageConnect))
|
||||
if err != nil {
|
||||
log.Error(err, "")
|
||||
os.Exit(1)
|
||||
}
|
||||
return shouldManageConnectBool
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func shouldAutoRestartDeployments() bool {
|
||||
shouldAutoRestartDeployments, found := os.LookupEnv(restartDeploymentsEnvVariable)
|
||||
if found {
|
||||
shouldAutoRestartDeploymentsBool, err := strconv.ParseBool(strings.ToLower(shouldAutoRestartDeployments))
|
||||
if err != nil {
|
||||
log.Error(err, "")
|
||||
os.Exit(1)
|
||||
}
|
||||
return shouldAutoRestartDeploymentsBool
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
68
deploy/connect/deployment.yaml
Normal file
68
deploy/connect/deployment.yaml
Normal file
@@ -0,0 +1,68 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: onepassword-connect
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: onepassword-connect
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: onepassword-connect
|
||||
version: "1.0.0"
|
||||
spec:
|
||||
volumes:
|
||||
- name: shared-data
|
||||
emptyDir: {}
|
||||
- name: credentials
|
||||
secret:
|
||||
secretName: op-credentials
|
||||
initContainers:
|
||||
- name: sqlite-permissions
|
||||
image: alpine:3.12
|
||||
command:
|
||||
- "/bin/sh"
|
||||
- "-c"
|
||||
args:
|
||||
- "mkdir -p /home/opuser/.op/data && chown -R 999 /home/opuser && chmod -R 700 /home/opuser && chmod -f -R 600 /home/opuser/.op/config || :"
|
||||
volumeMounts:
|
||||
- mountPath: /home/opuser/.op/data
|
||||
name: shared-data
|
||||
containers:
|
||||
- name: connect-api
|
||||
image: 1password/connect-api:latest
|
||||
resources:
|
||||
limits:
|
||||
memory: "128Mi"
|
||||
cpu: "0.2"
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
env:
|
||||
- name: OP_SESSION
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: op-credentials
|
||||
key: op-session
|
||||
volumeMounts:
|
||||
- mountPath: /home/opuser/.op/data
|
||||
name: shared-data
|
||||
- name: connect-sync
|
||||
image: 1password/connect-sync:latest
|
||||
resources:
|
||||
limits:
|
||||
memory: "128Mi"
|
||||
cpu: "0.2"
|
||||
ports:
|
||||
- containerPort: 8081
|
||||
env:
|
||||
- name: OP_HTTP_PORT
|
||||
value: "8081"
|
||||
- name: OP_SESSION
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: op-credentials
|
||||
key: op-session
|
||||
volumeMounts:
|
||||
- mountPath: /home/opuser/.op/data
|
||||
name: shared-data
|
15
deploy/connect/service.yaml
Normal file
15
deploy/connect/service.yaml
Normal file
@@ -0,0 +1,15 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: onepassword-connect
|
||||
spec:
|
||||
type: NodePort
|
||||
selector:
|
||||
app: onepassword-connect
|
||||
ports:
|
||||
- port: 8080
|
||||
name: connect-api
|
||||
nodePort: 31080
|
||||
- port: 8081
|
||||
name: connect-sync
|
||||
nodePort: 31081
|
@@ -1,4 +1,4 @@
|
||||
apiVersion: apiextensions.k8s.io/v1beta1
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: onepassworditems.onepassword.com
|
||||
@@ -10,36 +10,36 @@ spec:
|
||||
plural: onepassworditems
|
||||
singular: onepassworditem
|
||||
scope: Namespaced
|
||||
subresources:
|
||||
status: {}
|
||||
validation:
|
||||
openAPIV3Schema:
|
||||
description: OnePasswordItem is the Schema for the onepassworditems API
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: OnePasswordItemSpec defines the desired state of OnePasswordItem
|
||||
properties:
|
||||
item_path:
|
||||
type: string
|
||||
type: object
|
||||
status:
|
||||
description: OnePasswordItemStatus defines the observed state of OnePasswordItem
|
||||
type: object
|
||||
type: object
|
||||
version: v1
|
||||
versions:
|
||||
- name: v1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: OnePasswordItem is the Schema for the onepassworditems API
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: OnePasswordItemSpec defines the desired state of OnePasswordItem
|
||||
properties:
|
||||
itemPath:
|
||||
type: string
|
||||
type: object
|
||||
status:
|
||||
description: OnePasswordItemStatus defines the observed state of OnePasswordItem
|
||||
type: object
|
||||
type:
|
||||
description: 'Kubernetes secret type. More info: https://kubernetes.io/docs/concepts/configuration/secret/#secret-types'
|
||||
type: string
|
||||
type: object
|
||||
|
@@ -17,7 +17,6 @@ spec:
|
||||
- name: onepassword-connect-operator
|
||||
image: 1password/onepassword-operator
|
||||
command: ["/manager"]
|
||||
imagePullPolicy: Never
|
||||
env:
|
||||
- name: WATCH_NAMESPACE
|
||||
value: "default"
|
||||
@@ -36,3 +35,5 @@ spec:
|
||||
secretKeyRef:
|
||||
name: onepassword-token
|
||||
key: token
|
||||
- name: AUTO_RESTART
|
||||
value: "false"
|
||||
|
@@ -16,9 +16,7 @@ spec:
|
||||
containers:
|
||||
- name: onepassword-connect-operator
|
||||
image: 1password/onepassword-operator
|
||||
command:
|
||||
- onepassword-connect-operator
|
||||
imagePullPolicy: Never
|
||||
command: ["/manager"]
|
||||
env:
|
||||
- name: WATCH_NAMESPACE
|
||||
value: "default,development"
|
||||
@@ -29,7 +27,7 @@ spec:
|
||||
- name: OPERATOR_NAME
|
||||
value: "onepassword-connect-operator"
|
||||
- name: OP_CONNECT_HOST
|
||||
value: "http://secret-service:8080"
|
||||
value: "http://onepassword-connect:8080"
|
||||
- name: POLLING_INTERVAL
|
||||
value: "10"
|
||||
- name: OP_CONNECT_TOKEN
|
||||
@@ -37,3 +35,5 @@ spec:
|
||||
secretKeyRef:
|
||||
name: onepassword-token
|
||||
key: token
|
||||
- name: AUTO_RESTART
|
||||
value: "false"
|
||||
|
@@ -3,7 +3,7 @@ kind: ServiceAccount
|
||||
metadata:
|
||||
name: onepassword-connect-operator
|
||||
---
|
||||
kind: RoleBinding
|
||||
kind: ClusterRoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: onepassword-connect-operator-default
|
||||
@@ -13,12 +13,12 @@ subjects:
|
||||
name: onepassword-connect-operator
|
||||
namespace: default
|
||||
roleRef:
|
||||
kind: Role
|
||||
kind: ClusterRole
|
||||
name: onepassword-connect-operator
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: onepassword-connect-operator
|
||||
@@ -34,6 +34,7 @@ rules:
|
||||
- events
|
||||
- configmaps
|
||||
- secrets
|
||||
- namespaces
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
|
@@ -3,7 +3,7 @@ kind: ServiceAccount
|
||||
metadata:
|
||||
name: onepassword-connect-operator
|
||||
---
|
||||
kind: RoleBinding
|
||||
kind: ClusterRoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: onepassword-connect-operator-default
|
||||
@@ -17,7 +17,7 @@ roleRef:
|
||||
name: onepassword-connect-operator
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
---
|
||||
kind: RoleBinding
|
||||
kind: ClusterRoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: onepassword-connect-operator-development
|
||||
@@ -48,6 +48,7 @@ rules:
|
||||
- events
|
||||
- configmaps
|
||||
- secrets
|
||||
- namespaces
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
|
8
go.mod
8
go.mod
@@ -3,15 +3,11 @@ module github.com/1Password/onepassword-operator
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/1Password/connect-sdk-go v0.0.2
|
||||
github.com/go-logr/logr v0.1.0 // indirect
|
||||
github.com/1Password/connect-sdk-go v1.2.0
|
||||
github.com/operator-framework/operator-sdk v0.19.0
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/prometheus/common v0.14.0 // indirect
|
||||
github.com/sirupsen/logrus v1.7.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/stretchr/testify v1.6.1
|
||||
go.etcd.io/etcd v3.3.25+incompatible // indirect
|
||||
github.com/stretchr/testify v1.7.0
|
||||
k8s.io/api v0.18.2
|
||||
k8s.io/apimachinery v0.18.2
|
||||
k8s.io/client-go v12.0.0+incompatible
|
||||
|
193
go.sum
193
go.sum
@@ -1,5 +1,4 @@
|
||||
bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8=
|
||||
bou.ke/monkey v1.0.1/go.mod h1:FgHuK96Rv2Nlf+0u1OOVDpCMdsWyOFmeeketDHE7LIg=
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw=
|
||||
@@ -19,10 +18,10 @@ cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiy
|
||||
cloud.google.com/go/storage v1.3.0/go.mod h1:9IAwXhoyBJ7z9LcAwkj0/7NnPzYaPeZxxVp3zm+5IqA=
|
||||
contrib.go.opencensus.io/exporter/ocagent v0.6.0/go.mod h1:zmKjrJcdo0aYcVS7bmEeSEBLPA9YJp5bjrofdU3pIXs=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/1Password/connect-sdk-go v0.0.1 h1:qsFZQDQ+JirZRwSom/p6zzNqkkcYAYx4EXivUyPhvBo=
|
||||
github.com/1Password/connect-sdk-go v0.0.1/go.mod h1:br2BWk2sqgJFnOFK5WSDfBBmwQ6E7hV9LoPqrtHGRNY=
|
||||
github.com/1Password/connect-sdk-go v0.0.2 h1:IBamxGS17zItC9TRwp/0G0Fh1GRV3mqOkcWvpK05Mx8=
|
||||
github.com/1Password/connect-sdk-go v0.0.2/go.mod h1:br2BWk2sqgJFnOFK5WSDfBBmwQ6E7hV9LoPqrtHGRNY=
|
||||
github.com/1Password/connect-sdk-go v1.0.1 h1:BOeMIxVk6/ISmLNWUkSxEbVI7tNr5+aNXIobMM0/I0U=
|
||||
github.com/1Password/connect-sdk-go v1.0.1/go.mod h1:br2BWk2sqgJFnOFK5WSDfBBmwQ6E7hV9LoPqrtHGRNY=
|
||||
github.com/1Password/connect-sdk-go v1.2.0 h1:WbIvmbDUpA89nyH0l3LF2iRSFJAv86d2D7IjVNjw6iw=
|
||||
github.com/1Password/connect-sdk-go v1.2.0/go.mod h1:qK2bF/GweAq812xj+HGfbauaE6cKX1MXfKhpAvoHEq8=
|
||||
github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4=
|
||||
github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc=
|
||||
github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
@@ -46,6 +45,7 @@ github.com/Azure/go-autorest/autorest/date v0.2.0 h1:yW+Zlqf26583pE43KhfnhFcdmSW
|
||||
github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.3.0 h1:qJumjCaCudz+OcqE9/XtEPfvtOjOmKaui4EOpFI6zZc=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=
|
||||
github.com/Azure/go-autorest/autorest/to v0.3.1-0.20191028180845-3492b2aff503/go.mod h1:MgwOyqaIuKdG4TL/2ywSsIWKAfJfgHDo8ObuUk3t5sA=
|
||||
github.com/Azure/go-autorest/autorest/validation v0.2.1-0.20191028180845-3492b2aff503/go.mod h1:3EEqHnBxQGHXRYq3HT1WyXAvT7LLY3tl70hw6tQIbjI=
|
||||
@@ -53,18 +53,17 @@ github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1Gn
|
||||
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
|
||||
github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k=
|
||||
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/DATA-DOG/go-sqlmock v1.4.1/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
||||
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
|
||||
github.com/HdrHistogram/hdrhistogram-go v1.0.1 h1:GX8GAYDuhlFQnI2fRDHQhTlkHMz8bEn0jTI6LJU0mpw=
|
||||
github.com/HdrHistogram/hdrhistogram-go v1.0.1/go.mod h1:BWJ+nMSHY3L41Zj7CA3uXnloDp7xxV0YvstAE7nKTaM=
|
||||
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
|
||||
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E=
|
||||
github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
|
||||
github.com/Masterminds/semver/v3 v3.0.3/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||
github.com/Masterminds/semver/v3 v3.1.0/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||
github.com/Masterminds/sprig/v3 v3.0.2/go.mod h1:oesJ8kPONMONaZgtiHNzUShJbksypC5kWczhZAf6+aU=
|
||||
github.com/Masterminds/sprig/v3 v3.1.0/go.mod h1:ONGMf7UfYGAbMXCZmQLy8x3lCDIPrEZE/rU8pmrbihA=
|
||||
github.com/Masterminds/squirrel v1.2.0/go.mod h1:yaPeOnPG5ZRwL9oKdTsO/prlkPbXWZlRVMQ/gGlzIuA=
|
||||
github.com/Masterminds/vcs v1.13.1/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA=
|
||||
@@ -131,15 +130,12 @@ github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnweb
|
||||
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
|
||||
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
|
||||
github.com/brancz/gojsontoyaml v0.0.0-20190425155809-e8bd32d46b3d/go.mod h1:IyUJYN1gvWjtLF5ZuygmxbnsAyP3aJS6cHzIuZY50B0=
|
||||
github.com/brancz/gojsontoyaml v0.0.0-20191212081931-bf2969bbd742/go.mod h1:IyUJYN1gvWjtLF5ZuygmxbnsAyP3aJS6cHzIuZY50B0=
|
||||
github.com/brancz/kube-rbac-proxy v0.5.0/go.mod h1:cL2VjiIFGS90Cjh5ZZ8+It6tMcBt8rwvuw2J6Mamnl0=
|
||||
github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk=
|
||||
github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
|
||||
github.com/bugsnag/bugsnag-go v1.5.3/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
|
||||
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
|
||||
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
|
||||
github.com/bugsnag/panicwrap v1.2.0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
|
||||
github.com/campoy/embedmd v1.0.0/go.mod h1:oxyr9RCiSXg0M3VJ3ks0UGfp98BpSSGr0kpiX3MzVl8=
|
||||
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
|
||||
github.com/cenkalti/backoff v0.0.0-20181003080854-62661b46c409/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||
@@ -156,7 +152,6 @@ github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6D
|
||||
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
|
||||
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
||||
github.com/cockroachdb/cockroach-go v0.0.0-20181001143604-e0a95dfd547c/go.mod h1:XGLbWH/ujMcbPbhZq52Nv6UrCghb1yGn//133kEsvDk=
|
||||
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
|
||||
@@ -168,7 +163,6 @@ github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.
|
||||
github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
||||
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
|
||||
github.com/containerd/continuity v0.0.0-20200107194136-26c1120b8d41/go.mod h1:Dq467ZllaHgAtVp4p1xUQWBrFXR9s/wyoTpG8zOJGkY=
|
||||
github.com/containerd/continuity v0.0.0-20200228182428-0f16d7a0959c/go.mod h1:Dq467ZllaHgAtVp4p1xUQWBrFXR9s/wyoTpG8zOJGkY=
|
||||
github.com/containerd/continuity v0.0.0-20200413184840-d3ef23f19fbb/go.mod h1:Dq467ZllaHgAtVp4p1xUQWBrFXR9s/wyoTpG8zOJGkY=
|
||||
github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=
|
||||
github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=
|
||||
@@ -177,27 +171,20 @@ github.com/containerd/ttrpc v1.0.1/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8h
|
||||
github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
|
||||
github.com/coreos/bbolt v1.3.1-coreos.6/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/bbolt v1.3.3 h1:n6AiVyVRKQFNb6mJlwESEvvLoDyiTzXX7ORAUlkeBdY=
|
||||
github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/etcd v3.3.15+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/etcd v3.3.17+incompatible h1:f/Z3EoDSx1yjaIjLQGo1diYUlQYSBrrAQ5vP8NjwXwo=
|
||||
github.com/coreos/etcd v3.3.17+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||
github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/coreos/prometheus-operator v0.38.0 h1:gF2xYIfO09XLFdyEecND46uihQ2KTaDwTozRZpXLtN4=
|
||||
github.com/coreos/prometheus-operator v0.38.0/go.mod h1:xZC7/TgeC0/mBaJk+1H9dbHaiEvLYHgX6Mi1h40UPh8=
|
||||
github.com/coreos/prometheus-operator v0.38.1-0.20200424145508-7e176fda06cc h1:nMbUjGuF7UzVluucix/vsy4973BNdEiT/aX6kFtskKM=
|
||||
github.com/coreos/prometheus-operator v0.38.1-0.20200424145508-7e176fda06cc/go.mod h1:erio69w1R/aC14D5nfvAXSlE8FT8jt2Hnavc50Dp33A=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
@@ -244,11 +231,9 @@ github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avu
|
||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
|
||||
github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI=
|
||||
github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=
|
||||
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
|
||||
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
|
||||
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
|
||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
||||
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
@@ -269,7 +254,6 @@ github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT
|
||||
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/evanphx/json-patch v4.1.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
@@ -284,16 +268,14 @@ github.com/fatih/structtag v1.1.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4
|
||||
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
|
||||
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
|
||||
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsouza/fake-gcs-server v1.7.0/go.mod h1:5XIRs4YvwNbNoz+1JF8j6KLAyDh7RHGAyAK3EP2EsNk=
|
||||
github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
|
||||
github.com/garyburd/redigo v1.6.0/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
|
||||
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I=
|
||||
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||
github.com/go-bindata/go-bindata v3.1.2+incompatible/go.mod h1:xK8Dsgwmeed+BBsSy2XTopBn/8uK2HWuGSnA11C3Joo=
|
||||
github.com/go-bindata/go-bindata/v3 v3.1.3/go.mod h1:1/zrpXsLD8YDIbhZRqXzm1Ghc7NhEvIN9+Z6R5/xH4I=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||
@@ -346,7 +328,6 @@ github.com/go-openapi/spec v0.17.2/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsd
|
||||
github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
|
||||
github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY=
|
||||
github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
|
||||
github.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
|
||||
github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
|
||||
github.com/go-openapi/strfmt v0.17.2/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
|
||||
github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
|
||||
@@ -372,18 +353,13 @@ github.com/gobuffalo/envy v1.7.1/go.mod h1:FurDp9+EDPE4aIUS3ZLyD+7/9fpx7YRt/ukY6
|
||||
github.com/gobuffalo/flect v0.1.5/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80=
|
||||
github.com/gobuffalo/flect v0.2.0/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80=
|
||||
github.com/gobuffalo/flect v0.2.1/go.mod h1:vmkQwuZYhN5Pc4ljYQZzP+1sq+NEkK+lh20jmEmX3jc=
|
||||
github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs=
|
||||
github.com/gobuffalo/logger v1.0.1/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs=
|
||||
github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q=
|
||||
github.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIaviy289eRuk=
|
||||
github.com/gobuffalo/packr/v2 v2.5.1/go.mod h1:8f9c96ITobJlPzI44jj+4tHnEKNt0xXWSVlXRN9X1Iw=
|
||||
github.com/gobuffalo/packr/v2 v2.7.1/go.mod h1:qYEvAazPaVxy7Y7KR0W8qYEE+RymX74kETFqjFoFlOc=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/gocql/gocql v0.0.0-20190301043612-f6df8288f9b4/go.mod h1:4Fw1eo5iaEhDUs8XyuhSVCVy52Jq3L+/3GJgYkwc+/0=
|
||||
github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=
|
||||
github.com/gofrs/flock v0.7.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
||||
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
|
||||
github.com/gogo/protobuf v0.0.0-20171007142547-342cbe0a0415/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
@@ -411,13 +387,11 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
@@ -435,8 +409,6 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
||||
@@ -465,7 +437,6 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m
|
||||
github.com/googleapis/gnostic v0.0.0-20170426233943-68f4ded48ba9/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||
github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||
github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||
github.com/googleapis/gnostic v0.3.1 h1:WeAefnSUHlBb0iJKwxFDZdbfGwkd7xRNuV+IpXMJhYk=
|
||||
github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU=
|
||||
github.com/gophercloud/gophercloud v0.1.0 h1:P/nh25+rzXouhytV2pUHBb65fnds26Ghl8/391+sT5o=
|
||||
@@ -478,12 +449,10 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORR
|
||||
github.com/gopherjs/gopherjs v0.0.0-20191106031601-ce3c9ade29de/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
|
||||
github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
|
||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.7.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
|
||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
@@ -503,7 +472,6 @@ github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.12.1 h1:zCy2xE9ablevUOrUZc3Dl72Dt+ya2FNAvC2yLYMHzi4=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.12.1/go.mod h1:8XEsbTttt/W+VvjtQhLACqCisSPWTxCZ7sBRjU6iH9c=
|
||||
github.com/grpc-ecosystem/grpc-health-probe v0.2.1-0.20181220223928-2bf0a5b182db/go.mod h1:uBKkC2RbarFsvS5jMJHpVhTLvGlGQj9JJwkaePE3FWI=
|
||||
github.com/grpc-ecosystem/grpc-health-probe v0.3.2/go.mod h1:izVOQ4RWbjUR6lm4nn+VLJyQ+FyaiGmprEYgI04Gs7U=
|
||||
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=
|
||||
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
|
||||
github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
|
||||
@@ -543,10 +511,8 @@ github.com/hashicorp/memberlist v0.1.4/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2p
|
||||
github.com/hashicorp/memberlist v0.1.5/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||
github.com/hashicorp/serf v0.8.5/go.mod h1:UpNcs7fFbpKIyZaUuSW6EPiH+eZC7OuyFD+wc1oal+k=
|
||||
github.com/helm/helm-2to3 v0.5.1/go.mod h1:AXFpQX2cSQpss+47ROPEeu7Sm4+CRJ1jKWCEQdHP3/c=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4=
|
||||
github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
|
||||
github.com/iancoleman/strcase v0.0.0-20190422225806-e506e3ef7365/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE=
|
||||
@@ -585,7 +551,6 @@ github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGn
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/jsonnet-bundler/jsonnet-bundler v0.2.0/go.mod h1:/by7P/OoohkI3q4CgSFqcoFsVY+IaNbzOVDknEsKDeU=
|
||||
github.com/jsonnet-bundler/jsonnet-bundler v0.3.1/go.mod h1:/by7P/OoohkI3q4CgSFqcoFsVY+IaNbzOVDknEsKDeU=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
@@ -593,7 +558,6 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
|
||||
github.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
@@ -606,6 +570,7 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kshvakov/clickhouse v1.3.5/go.mod h1:DMzX7FxRymoNkVgizH0DWAL8Cur7wHLgx3MUnGwJqpE=
|
||||
github.com/kylelemons/godebug v0.0.0-20160406211939-eadb3ce320cb/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
|
||||
@@ -630,10 +595,8 @@ github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
|
||||
github.com/maorfr/helm-plugin-utils v0.0.0-20200216074820-36d2fcf6ae86/go.mod h1:p3gwmRSFqbWw6plBpR0sKl3n3vpu8kX70gvCJKMvvCA=
|
||||
github.com/markbates/inflect v1.0.4/go.mod h1:1fR9+pO2KHEO9ZRtto13gDwwZaAKstQzferVeWqbgNs=
|
||||
github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho=
|
||||
github.com/martinlindhe/base36 v1.0.0/go.mod h1:+AtEs8xrBpCeYgSLoY/aJ6Wf37jtBuR0s35750M27+8=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc=
|
||||
@@ -647,7 +610,6 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky
|
||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-shellwords v1.0.9/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
|
||||
github.com/mattn/go-shellwords v1.0.10/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
|
||||
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
@@ -670,7 +632,6 @@ github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eI
|
||||
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||
github.com/mitchellh/hashstructure v0.0.0-20170609045927-2bca23e0e452/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ=
|
||||
github.com/mitchellh/hashstructure v1.0.0/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ=
|
||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
@@ -701,6 +662,7 @@ github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxzi
|
||||
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
|
||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||
github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
|
||||
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
||||
@@ -717,6 +679,7 @@ github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+
|
||||
github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw=
|
||||
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU=
|
||||
github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
|
||||
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
github.com/onsi/gomega v0.0.0-20190113212917-5533ce8a0da3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
@@ -726,6 +689,7 @@ github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.8.1 h1:C5Dqfs/LeauYDX0jJXIe2SWmwCbGzx9yF8C8xy3Lh34=
|
||||
github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
|
||||
github.com/onsi/gomega v1.9.0 h1:R1uwffexN6Pr340GtYRIdZmAiN4J+iw6WG4wog1DUXg=
|
||||
github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
||||
github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
@@ -736,12 +700,8 @@ github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zM
|
||||
github.com/opencontainers/image-spec v1.0.2-0.20190823105129-775207bd45b6/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||
github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
|
||||
github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
|
||||
github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
|
||||
github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/runtime-spec v0.1.2-0.20190618234442-a950415649c7/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
|
||||
github.com/openshift/api v0.0.0-20200205133042-34f0ec8dab87/go.mod h1:fT6U/JfG8uZzemTRwZA2kBDJP5nWz7v05UHnty/D+pk=
|
||||
github.com/openshift/client-go v0.0.0-20190923180330-3b6373338c9b/go.mod h1:6rzn+JTr7+WYS2E1TExP4gByoABxMznR6y2SnUIkmxk=
|
||||
github.com/openshift/origin v0.0.0-20160503220234-8f127d736703/go.mod h1:0Rox5r9C8aQn6j1oAOQ0c1uC86mYbUFObzjBRvUKHII=
|
||||
github.com/openshift/prom-label-proxy v0.1.1-0.20191016113035-b8153a7f39f1/go.mod h1:p5MuxzsYP1JPsNGwtjtcgRHHlGziCJJfztff91nNixw=
|
||||
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
|
||||
@@ -755,38 +715,14 @@ github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxS
|
||||
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
|
||||
github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
|
||||
github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
|
||||
github.com/operator-framework/api v0.1.1/go.mod h1:yzNYR7qyJqRGOOp+bT6Z/iYSbSPNxeh3Si93Gx/3OBY=
|
||||
github.com/operator-framework/api v0.3.4/go.mod h1:TmRmw+8XOUaDPq6SP9gA8cIexNf/Pq8LMFY7YaKQFTs=
|
||||
github.com/operator-framework/api v0.3.7-0.20200528122852-759ca0d84007/go.mod h1:Xbje9x0SHmh0nihE21kpesB38vk3cyxnE6JdDS8Jo1Q=
|
||||
github.com/operator-framework/api v0.3.7-0.20200602203552-431198de9fc2/go.mod h1:Xbje9x0SHmh0nihE21kpesB38vk3cyxnE6JdDS8Jo1Q=
|
||||
github.com/operator-framework/api v0.3.8/go.mod h1:Xbje9x0SHmh0nihE21kpesB38vk3cyxnE6JdDS8Jo1Q=
|
||||
github.com/operator-framework/api v0.3.13/go.mod h1:Xbje9x0SHmh0nihE21kpesB38vk3cyxnE6JdDS8Jo1Q=
|
||||
github.com/operator-framework/operator-lifecycle-manager v0.0.0-20200321030439-57b580e57e88/go.mod h1:7Ut8p9jJ8C6RZyyhZfZypmlibCIJwK5Wcc+WZDgLkOA=
|
||||
github.com/operator-framework/operator-registry v1.5.3/go.mod h1:agrQlkWOo1q8U1SAaLSS2WQ+Z9vswNT2M2HFib9iuLY=
|
||||
github.com/operator-framework/operator-registry v1.6.1/go.mod h1:sx4wWMiZtYhlUiaKscg3QQUPPM/c1bkrAs4n4KipDb4=
|
||||
github.com/operator-framework/operator-registry v1.6.2-0.20200330184612-11867930adb5/go.mod h1:SHff373z8asEkPo6aWpN0qId4Y/feQTjZxRF8PRhti8=
|
||||
github.com/operator-framework/operator-registry v1.12.1/go.mod h1:rf4b/h77GUv1+geiej2KzGRQr8iBLF4dXNwr5AuGkrQ=
|
||||
github.com/operator-framework/operator-registry v1.12.4/go.mod h1:JChIivJVLE1wRbgIhDFzYQYT9yosa2wd6qiTyMuG5mg=
|
||||
github.com/operator-framework/operator-registry v1.12.6-0.20200611222234-275301b779f8/go.mod h1:loVINznYhgBIkmv83kU4yee88RS0BBk+hqOw9r4bhJk=
|
||||
github.com/operator-framework/operator-registry v1.13.4/go.mod h1:YhnIzOVjRU2ZwZtzt+fjcjW8ujJaSFynBEu7QVKaSdU=
|
||||
github.com/operator-framework/operator-sdk v0.17.0 h1:+TTrGjXa+lm7g7Cm0UtFcgOjnw1x9/lBorydpsIIhOY=
|
||||
github.com/operator-framework/operator-sdk v0.17.0/go.mod h1:wmYi08aoUmtgfoUamURmssI4dkdFGNtSI1Egj+ZfBnk=
|
||||
github.com/operator-framework/operator-sdk v0.18.0 h1:YdtgXvjHu+f0hE/Nzvw9JIU3XvOZyp2Kd2cOLW486rU=
|
||||
github.com/operator-framework/operator-sdk v0.18.0/go.mod h1:xP/DNvnYnIoGK1bLKiD0s7aYZp2fa4AI6t1v3INaoZg=
|
||||
github.com/operator-framework/operator-sdk v0.19.0 h1:pnHx1EjcP20miuBpxFC2s+uuPeItcrmRR6bOFhm4D6c=
|
||||
github.com/operator-framework/operator-sdk v0.19.0/go.mod h1:8MR6CguLizat2RGjdSMifGwW6mEMwKqAtZnSUHJ6SxU=
|
||||
github.com/operator-framework/operator-sdk v0.19.4 h1:QI6k+WBDAXagx2OunEajQLa8LZwmRXu+x/SwmVZ/CCw=
|
||||
github.com/operator-framework/operator-sdk v0.19.4/go.mod h1:+gIlE/CfBGFGj51qJ2sLTPZWE1X27cKtjZ0m5vwY8Hw=
|
||||
github.com/operator-framework/operator-sdk v1.1.0 h1:te2CWGrgu0zPlefs3qbVb0ipUQ1zQ4lG27piXbBWn58=
|
||||
github.com/operator-framework/operator-sdk v1.2.0 h1:Ql/UXtQQz21juAwsWV5lctcFwsNI90GKdf1/saJZWj0=
|
||||
github.com/otiai10/copy v1.0.1/go.mod h1:8bMCJrAqOtN/d9oyh5HR7HhLQMvcGMpGdwRDYsfOCHc=
|
||||
github.com/otiai10/copy v1.0.2/go.mod h1:c7RpqBkwMom4bYTSkLSym4VSJz/XtncWRAj/J4PEIMY=
|
||||
github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw=
|
||||
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
|
||||
github.com/otiai10/curr v0.0.0-20190513014714-f5a3d24e5776/go.mod h1:3HNVkVOU7vZeFXocWuvtcS0XSFLcf2XUSDHkq9t1jU4=
|
||||
github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs=
|
||||
github.com/otiai10/mint v1.2.3/go.mod h1:YnfyPNhBvnY8bW4SGQHCs/aAFhkgySlMZbrF5U0bOVw=
|
||||
github.com/otiai10/mint v1.2.4/go.mod h1:d+b7n/0R3tdyUYYylALXpWQ/kTN+QobSq/4SRGBkR3M=
|
||||
github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
|
||||
github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc=
|
||||
github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
|
||||
@@ -879,7 +815,6 @@ github.com/rogpeppe/go-internal v1.3.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE
|
||||
github.com/rogpeppe/go-internal v1.4.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.5.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
|
||||
github.com/rubenv/sql-migrate v0.0.0-20191025130928-9355dd04f4b3/go.mod h1:WS0rl9eEliYI8DPnr3TOwz4439pay+qNgzJoVya/DmY=
|
||||
github.com/rubenv/sql-migrate v0.0.0-20200212082348-64f95ea68aa3/go.mod h1:rtQlpHw+eR6UrqaS3kX1VYeaCxzCVdimDS7g5Ln4pPc=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
@@ -906,8 +841,6 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd
|
||||
github.com/sirupsen/logrus v1.5.0 h1:1N5EYkVAPEywqZRJd7cwnRtCb6xJx7NH3T3WUTF980Q=
|
||||
github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
|
||||
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
@@ -954,6 +887,8 @@ github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
||||
github.com/thanos-io/thanos v0.11.0/go.mod h1:N/Yes7J68KqvmY+xM6J5CJqEvWIvKSR5sqGtmuD6wDc=
|
||||
github.com/tidwall/pretty v0.0.0-20180105212114-65a9db5fad51/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||
@@ -987,14 +922,10 @@ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q
|
||||
github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1/go.mod h1:QcJo0QPSfTONNIgpN5RA8prR7fF8nkF6cTWTcNerRO8=
|
||||
github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs=
|
||||
github.com/yvasiyarov/go-metrics v0.0.0-20150112132944-c25f46c4b940/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs=
|
||||
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA=
|
||||
github.com/yvasiyarov/gorelic v0.0.7/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA=
|
||||
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
|
||||
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20160601141957-9c099fbc30e9/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
|
||||
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
|
||||
gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b/go.mod h1:T3BPAOm2cqquPa0MKWeNkmOM5RQsRhkrwMWonFMN7fE=
|
||||
go.elastic.co/apm v1.5.0/go.mod h1:OdB9sPtM6Vt7oz3VXt7+KR96i9li74qrxBGHTQygFvk=
|
||||
@@ -1006,9 +937,6 @@ go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
|
||||
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738 h1:VcrIfasaLFkyjk6KNlXQSzO+B0fZcnECiDrKJsfxka0=
|
||||
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
|
||||
go.etcd.io/etcd v0.5.0-alpha.5 h1:VOolFSo3XgsmnYDLozjvZ6JL6AAwIDu1Yx1y+4EYLDo=
|
||||
go.etcd.io/etcd v3.3.25+incompatible h1:V1RzkZJj9LqsJRy+TUBgpWSbZXITLB819lstuTFoZOY=
|
||||
go.etcd.io/etcd v3.3.25+incompatible/go.mod h1:yaeTdrJi5lOmYerz05bd8+V7KubZs8YSFZfzsF9A6aI=
|
||||
go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
||||
go.mongodb.org/mongo-driver v1.1.0/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
||||
go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
||||
@@ -1032,6 +960,7 @@ go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/
|
||||
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
||||
go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=
|
||||
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||
go.uber.org/zap v0.0.0-20180814183419-67bc79d13d15/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
@@ -1055,7 +984,6 @@ golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
@@ -1082,12 +1010,14 @@ golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHl
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f h1:J5lckAjkw6qYlOZNj90mLYNTEKDvWeuc1yieZ8qUzUE=
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -1123,10 +1053,8 @@ golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 h1:rjwSpXsdiK0dV8/Naq3kAw9ymfAeJIyd0upUIElB+lI=
|
||||
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191021144547-ec77196f6094/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191028085509-fe3aa8a45271/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191112182307-2180aed22343/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
|
||||
@@ -1185,14 +1113,11 @@ golang.org/x/sys v0.0.0-20190922100055-0a153f010e69 h1:rOhMmluY6kLMhdnrivzec6lLg
|
||||
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191020212454-3e7259c5e7c2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191025021431-6c3a3bfe00ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191112214154-59a1497f0cea/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191113165036-4c7a9d0fe056/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -1212,8 +1137,6 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3
|
||||
golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
|
||||
@@ -1242,7 +1165,6 @@ golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgw
|
||||
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190706070813-72ffa07ba3db/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||
@@ -1263,13 +1185,13 @@ golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtn
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200115044656-831fdb1e1868/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200327195553-82bb89366a1e/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||
golang.org/x/tools v0.0.0-20200403190813-44a64ad78b9b h1:AFZdJUT7jJYXQEC29hYH/WZkoV7+KhwxQGmdZ19yYoY=
|
||||
golang.org/x/tools v0.0.0-20200403190813-44a64ad78b9b/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gomodules.xyz/jsonpatch/v2 v2.0.1 h1:xyiBuvkD2g5n7cYzx6u2sxQvsAy4QJsZFCzGVdzOXZ0=
|
||||
gomodules.xyz/jsonpatch/v2 v2.0.1/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU=
|
||||
@@ -1308,13 +1230,10 @@ google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||
google.golang.org/genproto v0.0.0-20190927181202-20e1ac93f88c/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||
google.golang.org/genproto v0.0.0-20191009194640-548a555dbc03/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9 h1:6XzpBoANz1NqMNfDXzc2QmHmbb1vyMsvRfoP5rM+K1I=
|
||||
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200117163144-32f20d992d24/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20200701001935-0939c5918c31/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
@@ -1331,20 +1250,13 @@ google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQ
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.0 h1:rRYRFMVgRv6E0D70Skyfsr28tDXIuuPZyWGMPdMcnXg=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v0.0.0-20200709232328-d8193ee9cc3e/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
@@ -1352,6 +1264,7 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
|
||||
gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
@@ -1381,7 +1294,6 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
@@ -1391,15 +1303,13 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
|
||||
helm.sh/helm/v3 v3.1.0/go.mod h1:WYsFJuMASa/4XUqLyv54s0U/f3mlAaRErGmyy4z921g=
|
||||
helm.sh/helm/v3 v3.1.2/go.mod h1:WYsFJuMASa/4XUqLyv54s0U/f3mlAaRErGmyy4z921g=
|
||||
helm.sh/helm/v3 v3.2.0/go.mod h1:ZaXz/vzktgwjyGGFbUWtIQkscfE7WYoRGP2szqAFHR0=
|
||||
helm.sh/helm/v3 v3.2.4/go.mod h1:ZaXz/vzktgwjyGGFbUWtIQkscfE7WYoRGP2szqAFHR0=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0=
|
||||
k8s.io/api v0.0.0-20190620084959-7cf5895f2711/go.mod h1:TBhBqb1AWbBQbW3XRusr7n7E4v2+5ZY8r8sAMnyFC5A=
|
||||
@@ -1407,25 +1317,10 @@ k8s.io/api v0.0.0-20190813020757-36bff7324fb7/go.mod h1:3Iy+myeAORNCLgjd/Xu9ebwN
|
||||
k8s.io/api v0.0.0-20190918155943-95b840bb6a1f/go.mod h1:uWuOHnjmNrtQomJrvEBg0c0HRNyQ+8KTEERVsK0PW48=
|
||||
k8s.io/api v0.0.0-20191115095533-47f6de673b26/go.mod h1:iA/8arsvelvo4IDqIhX4IbjTEKBGgvsf2OraTuRtLFU=
|
||||
k8s.io/api v0.0.0-20191122220107-b5267f2975e0/go.mod h1:vYpRfxYkMrmPPSesoHEkGNHxNKTk96REAwqm/inQbs0=
|
||||
k8s.io/api v0.16.7/go.mod h1:oUAiGRgo4t+5yqcxjOu5LoHT3wJ8JSbgczkaFYS5L7I=
|
||||
k8s.io/api v0.17.0/go.mod h1:npsyOePkeP0CPwyGfXDHxvypiYMJxBWAMpQxCaJ4ZxI=
|
||||
k8s.io/api v0.17.1/go.mod h1:zxiAc5y8Ngn4fmhWUtSxuUlkfz1ixT7j9wESokELzOg=
|
||||
k8s.io/api v0.17.2/go.mod h1:BS9fjjLc4CMuqfSO8vgbHPKMt5+SF0ET6u/RVDihTo4=
|
||||
k8s.io/api v0.17.3/go.mod h1:YZ0OTkuw7ipbe305fMpIdf3GLXZKRigjtZaV5gzC2J0=
|
||||
k8s.io/api v0.17.4 h1:HbwOhDapkguO8lTAE8OX3hdF2qp8GtpC9CW/MQATXXo=
|
||||
k8s.io/api v0.17.4/go.mod h1:5qxx6vjmwUVG2nHQTKGlLts8Tbok8PzHl4vHtVFuZCA=
|
||||
k8s.io/api v0.18.0/go.mod h1:q2HRQkfDzHMBZL9l/y9rH63PkQl4vae0xRT+8prbrK8=
|
||||
k8s.io/api v0.18.2 h1:wG5g5ZmSVgm5B+eHMIbI9EGATS2L8Z72rda19RIEgY8=
|
||||
k8s.io/api v0.18.2/go.mod h1:SJCWI7OLzhZSvbY7U8zwNl9UA4o1fizoug34OV/2r78=
|
||||
k8s.io/api v0.19.3 h1:GN6ntFnv44Vptj/b+OnMW7FmzkpDoIDLZRvKX3XH9aU=
|
||||
k8s.io/apiextensions-apiserver v0.0.0-20190918161926-8f644eb6e783/go.mod h1:xvae1SZB3E17UpV59AWc271W/Ph25N+bjPyR63X6tPY=
|
||||
k8s.io/apiextensions-apiserver v0.16.7/go.mod h1:6xYRp4trGp6eT5WZ6tPi/TB2nfWQCzwUvBlpg8iswe0=
|
||||
k8s.io/apiextensions-apiserver v0.17.0/go.mod h1:XiIFUakZywkUl54fVXa7QTEHcqQz9HG55nHd1DCoHj8=
|
||||
k8s.io/apiextensions-apiserver v0.17.2 h1:cP579D2hSZNuO/rZj9XFRzwJNYb41DbNANJb6Kolpss=
|
||||
k8s.io/apiextensions-apiserver v0.17.2/go.mod h1:4KdMpjkEjjDI2pPfBA15OscyNldHWdBCfsWMDWAmSTs=
|
||||
k8s.io/apiextensions-apiserver v0.17.3/go.mod h1:CJbCyMfkKftAd/X/V6OTHYhVn7zXnDdnkUjS1h0GTeY=
|
||||
k8s.io/apiextensions-apiserver v0.17.4 h1:ZKFnw3cJrGZ/9s6y+DerTF4FL+dmK0a04A++7JkmMho=
|
||||
k8s.io/apiextensions-apiserver v0.17.4/go.mod h1:rCbbbaFS/s3Qau3/1HbPlHblrWpFivoaLYccCffvQGI=
|
||||
k8s.io/apiextensions-apiserver v0.18.0/go.mod h1:18Cwn1Xws4xnWQNC00FLq1E350b9lUF+aOdIWDOZxgo=
|
||||
k8s.io/apiextensions-apiserver v0.18.2 h1:I4v3/jAuQC+89L3Z7dDgAiN4EOjN6sbm6iBqQwHTah8=
|
||||
k8s.io/apiextensions-apiserver v0.18.2/go.mod h1:q3faSnRGmYimiocj6cHQ1I3WpLqmDgJFlKL37fC4ZvY=
|
||||
@@ -1434,60 +1329,28 @@ k8s.io/apimachinery v0.0.0-20190809020650-423f5d784010/go.mod h1:Waf/xTS2FGRrgXC
|
||||
k8s.io/apimachinery v0.0.0-20190913080033-27d36303b655/go.mod h1:nL6pwRT8NgfF8TT68DBI8uEePRt89cSvoXUVqbkWHq4=
|
||||
k8s.io/apimachinery v0.0.0-20191115015347-3c7067801da2/go.mod h1:dXFS2zaQR8fyzuvRdJDHw2Aerij/yVGJSre0bZQSVJA=
|
||||
k8s.io/apimachinery v0.0.0-20191121175448-79c2a76c473a/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg=
|
||||
k8s.io/apimachinery v0.16.7/go.mod h1:Xk2vD2TRRpuWYLQNM6lT9R7DSFZUYG03SarNkbGrnKE=
|
||||
k8s.io/apimachinery v0.17.0/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg=
|
||||
k8s.io/apimachinery v0.17.1/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg=
|
||||
k8s.io/apimachinery v0.17.2/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg=
|
||||
k8s.io/apimachinery v0.17.3/go.mod h1:gxLnyZcGNdZTCLnq3fgzyg2A5BVCHTNDFrw8AmuJ+0g=
|
||||
k8s.io/apimachinery v0.17.4 h1:UzM+38cPUJnzqSQ+E1PY4YxMHIzQyCg29LOoGfo79Zw=
|
||||
k8s.io/apimachinery v0.17.4/go.mod h1:gxLnyZcGNdZTCLnq3fgzyg2A5BVCHTNDFrw8AmuJ+0g=
|
||||
k8s.io/apimachinery v0.18.0/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA=
|
||||
k8s.io/apimachinery v0.18.2 h1:44CmtbmkzVDAhCpRVSiP2R5PPrC2RtlIv/MoB8xpdRA=
|
||||
k8s.io/apimachinery v0.18.2/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA=
|
||||
k8s.io/apimachinery v0.19.2 h1:5Gy9vQpAGTKHPVOh5c4plE274X8D/6cuEiTO2zve7tc=
|
||||
k8s.io/apimachinery v0.19.3 h1:bpIQXlKjB4cB/oNpnNnV+BybGPR7iP5oYpsOTEJ4hgc=
|
||||
k8s.io/apiserver v0.0.0-20190918160949-bfa5e2e684ad/go.mod h1:XPCXEwhjaFN29a8NldXA901ElnKeKLrLtREO9ZhFyhg=
|
||||
k8s.io/apiserver v0.0.0-20191122221311-9d521947b1e1/go.mod h1:RbsZY5zzBIWnz4KbctZsTVjwIuOpTp4Z8oCgFHN4kZQ=
|
||||
k8s.io/apiserver v0.16.7/go.mod h1:/5zSatF30/L9zYfMTl55jzzOnx7r/gGv5a5wtRp8yAw=
|
||||
k8s.io/apiserver v0.17.0/go.mod h1:ABM+9x/prjINN6iiffRVNCBR2Wk7uY4z+EtEGZD48cg=
|
||||
k8s.io/apiserver v0.17.2/go.mod h1:lBmw/TtQdtxvrTk0e2cgtOxHizXI+d0mmGQURIHQZlo=
|
||||
k8s.io/apiserver v0.17.3/go.mod h1:iJtsPpu1ZpEnHaNawpSV0nYTGBhhX2dUlnn7/QS7QiY=
|
||||
k8s.io/apiserver v0.17.4/go.mod h1:5ZDQ6Xr5MNBxyi3iUZXS84QOhZl+W7Oq2us/29c0j9I=
|
||||
k8s.io/apiserver v0.18.0/go.mod h1:3S2O6FeBBd6XTo0njUrLxiqk8GNy6wWOftjhJcXYnjw=
|
||||
k8s.io/apiserver v0.18.2/go.mod h1:Xbh066NqrZO8cbsoenCwyDJ1OSi8Ag8I2lezeHxzwzw=
|
||||
k8s.io/autoscaler v0.0.0-20190607113959-1b4f1855cb8e/go.mod h1:QEXezc9uKPT91dwqhSJq3GNI3B1HxFRQHiku9kmrsSA=
|
||||
k8s.io/cli-runtime v0.17.2/go.mod h1:aa8t9ziyQdbkuizkNLAw3qe3srSyWh9zlSB7zTqRNPI=
|
||||
k8s.io/cli-runtime v0.17.3/go.mod h1:X7idckYphH4SZflgNpOOViSxetiMj6xI0viMAjM81TA=
|
||||
k8s.io/cli-runtime v0.17.4/go.mod h1:IVW4zrKKx/8gBgNNkhiUIc7nZbVVNhc1+HcQh+PiNHc=
|
||||
k8s.io/cli-runtime v0.18.0/go.mod h1:1eXfmBsIJosjn9LjEBUd2WVPoPAY9XGTqTFcPMIBsUQ=
|
||||
k8s.io/cli-runtime v0.18.2/go.mod h1:yfFR2sQQzDsV0VEKGZtrJwEy4hLZ2oj4ZIfodgxAHWQ=
|
||||
k8s.io/client-go v0.17.4 h1:VVdVbpTY70jiNHS1eiFkUt7ZIJX3txd29nDxxXH4en8=
|
||||
k8s.io/client-go v0.17.4/go.mod h1:ouF6o5pz3is8qU0/qYL2RnoxOPqgfuidYLowytyLJmc=
|
||||
k8s.io/client-go v0.18.2 h1:aLB0iaD4nmwh7arT2wIn+lMnAq7OswjaejkQ8p9bBYE=
|
||||
k8s.io/client-go v0.18.2/go.mod h1:Xcm5wVGXX9HAA2JJ2sSBUn3tCJ+4SVlCbl2MNNv+CIU=
|
||||
k8s.io/code-generator v0.0.0-20190912054826-cd179ad6a269/go.mod h1:V5BD6M4CyaN5m+VthcclXWsVcT1Hu+glwa1bi3MIsyE=
|
||||
k8s.io/code-generator v0.16.7/go.mod h1:wFdrXdVi/UC+xIfLi+4l9elsTT/uEF61IfcN2wOLULQ=
|
||||
k8s.io/code-generator v0.17.0/go.mod h1:DVmfPQgxQENqDIzVR2ddLXMH34qeszkKSdH/N+s+38s=
|
||||
k8s.io/code-generator v0.17.1/go.mod h1:DVmfPQgxQENqDIzVR2ddLXMH34qeszkKSdH/N+s+38s=
|
||||
k8s.io/code-generator v0.17.2/go.mod h1:DVmfPQgxQENqDIzVR2ddLXMH34qeszkKSdH/N+s+38s=
|
||||
k8s.io/code-generator v0.17.3/go.mod h1:l8BLVwASXQZTo2xamW5mQNFCe1XPiAesVq7Y1t7PiQQ=
|
||||
k8s.io/code-generator v0.17.4/go.mod h1:l8BLVwASXQZTo2xamW5mQNFCe1XPiAesVq7Y1t7PiQQ=
|
||||
k8s.io/code-generator v0.18.0/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc=
|
||||
k8s.io/code-generator v0.18.2/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc=
|
||||
k8s.io/component-base v0.0.0-20190918160511-547f6c5d7090/go.mod h1:933PBGtQFJky3TEwYx4aEPZ4IxqhWh3R6DCmzqIn1hA=
|
||||
k8s.io/component-base v0.0.0-20191122220729-2684fb322cb9/go.mod h1:NFuUusy/X4Tk21m21tcNUihnmp4OI7lXU7/xA+rYXkc=
|
||||
k8s.io/component-base v0.16.7/go.mod h1:ikdyfezOFMu5O0qJjy/Y9eXwj+fV3pVwdmt0ulVcIR0=
|
||||
k8s.io/component-base v0.17.0/go.mod h1:rKuRAokNMY2nn2A6LP/MiwpoaMRHpfRnrPaUJJj1Yoc=
|
||||
k8s.io/component-base v0.17.2/go.mod h1:zMPW3g5aH7cHJpKYQ/ZsGMcgbsA/VyhEugF3QT1awLs=
|
||||
k8s.io/component-base v0.17.3/go.mod h1:GeQf4BrgelWm64PXkIXiPh/XS0hnO42d9gx9BtbZRp8=
|
||||
k8s.io/component-base v0.17.4/go.mod h1:5BRqHMbbQPm2kKu35v3G+CpVq4K0RJKC7TRioF0I9lE=
|
||||
k8s.io/component-base v0.18.0/go.mod h1:u3BCg0z1uskkzrnAKFzulmYaEpZF7XC9Pf/uFyb1v2c=
|
||||
k8s.io/component-base v0.18.2/go.mod h1:kqLlMuhJNHQ9lz8Z7V5bxUUtjFZnrypArGl58gmDfUM=
|
||||
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/gengo v0.0.0-20191010091904-7fa3014cb28f/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/gengo v0.0.0-20200114144118-36b2048a9120/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/helm v2.16.3+incompatible/go.mod h1:LZzlS4LQBHfciFOurYBFkCMTaZ0D1l+p0teMg7TSULI=
|
||||
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
k8s.io/klog v0.3.1/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
@@ -1495,7 +1358,6 @@ k8s.io/klog v0.3.3/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
k8s.io/klog v0.4.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
|
||||
k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
|
||||
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
|
||||
k8s.io/kube-aggregator v0.17.3/go.mod h1:1dMwMFQbmH76RKF0614L7dNenMl3dwnUJuOOyZ3GMXA=
|
||||
k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc=
|
||||
k8s.io/kube-openapi v0.0.0-20190320154901-5e45bb682580/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc=
|
||||
k8s.io/kube-openapi v0.0.0-20190709113604-33be087ad058/go.mod h1:nfDlWeOsu3pUf4yWGL+ERqohP4YsZcBJXWMK+gkzOA4=
|
||||
@@ -1506,17 +1368,10 @@ k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c h1:/KUFqjjqAcY4Us6luF5RDN
|
||||
k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E=
|
||||
k8s.io/kube-state-metrics v1.7.2 h1:6vdtgXrrRRMSgnyDmgua+qvgCYv954JNfxXAtDkeLVQ=
|
||||
k8s.io/kube-state-metrics v1.7.2/go.mod h1:U2Y6DRi07sS85rmVPmBFlmv+2peBcL8IWGjM+IjYA/E=
|
||||
k8s.io/kubectl v0.17.2/go.mod h1:y4rfLV0n6aPmvbRCqZQjvOp3ezxsFgpqL+zF5jH/lxk=
|
||||
k8s.io/kubectl v0.17.3/go.mod h1:NUn4IBY7f7yCMwSop2HCXlw/MVYP4HJBiUmOR3n9w28=
|
||||
k8s.io/kubectl v0.17.4 h1:Ts0CvqvIVceS4RTVXgWMH+YqtieLAzyS2T9eoz8uDQ0=
|
||||
k8s.io/kubectl v0.17.4/go.mod h1:im5QWmh6fvtmJkkNm4HToLe8z9aM3jihYK5X/wOybcY=
|
||||
k8s.io/kubectl v0.18.0/go.mod h1:LOkWx9Z5DXMEg5KtOjHhRiC1fqJPLyCr3KtQgEolCkU=
|
||||
k8s.io/kubectl v0.18.2 h1:9jnGSOC2DDVZmMUTMi0D1aed438mfQcgqa5TAzVjA1k=
|
||||
k8s.io/kubectl v0.18.2/go.mod h1:OdgFa3AlsPKRpFFYE7ICTwulXOcMGXHTc+UKhHKvrb4=
|
||||
k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
|
||||
k8s.io/metrics v0.17.2/go.mod h1:3TkNHET4ROd+NfzNxkjoVfQ0Ob4iZnaHmSEA4vYpwLw=
|
||||
k8s.io/metrics v0.17.3/go.mod h1:HEJGy1fhHOjHggW9rMDBJBD3YuGroH3Y1pnIRw9FFaI=
|
||||
k8s.io/metrics v0.17.4/go.mod h1:6rylW2iD3M9VppnEAAtJASY1XS8Pt9tcYh+tHxBeV3I=
|
||||
k8s.io/metrics v0.18.0/go.mod h1:8aYTW18koXqjLVKL7Ds05RPMX9ipJZI3mywYvBOxXd4=
|
||||
k8s.io/metrics v0.18.2/go.mod h1:qga8E7QfYNR9Q89cSCAjinC9pTZ7yv1XSVGUB0vJypg=
|
||||
k8s.io/utils v0.0.0-20190308190857-21c4ce38f2a7/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3oVhZz7sSzSiPnVZayjIX0=
|
||||
@@ -1535,22 +1390,16 @@ modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/letsencrypt v0.0.3/go.mod h1:buyQKZ6IXrRnB7TdkHP0RyEybLx18HHyOSoTyoOLqNY=
|
||||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.7/go.mod h1:PHgbrJT7lCHcxMU+mDHEm+nx46H4zuuHZkDP6icnhu0=
|
||||
sigs.k8s.io/controller-runtime v0.5.2 h1:pyXbUfoTo+HA3jeIfr0vgi+1WtmNh0CwlcnQGLXwsSw=
|
||||
sigs.k8s.io/controller-runtime v0.5.2/go.mod h1:JZUwSMVbxDupo0lTJSSFP5pimEyxGynROImSsqIOx1A=
|
||||
sigs.k8s.io/controller-runtime v0.6.0 h1:Fzna3DY7c4BIP6KwfSlrfnj20DJ+SeMBK8HSFvOk9NM=
|
||||
sigs.k8s.io/controller-runtime v0.6.0/go.mod h1:CpYf5pdNY/B352A1TFLAS2JVSlnGQ5O2cftPHndTroo=
|
||||
sigs.k8s.io/controller-tools v0.2.4/go.mod h1:m/ztfQNocGYBgTTCmFdnK94uVvgxeZeE3LtJvd/jIzA=
|
||||
sigs.k8s.io/controller-tools v0.2.8/go.mod h1:9VKHPszmf2DHz/QmHkcfZoewO6BL7pPs9uAiBVsaJSE=
|
||||
sigs.k8s.io/controller-tools v0.3.0/go.mod h1:enhtKGfxZD1GFEoMgP8Fdbu+uKQ/cq1/WGJhdVChfvI=
|
||||
sigs.k8s.io/kubebuilder v1.0.9-0.20200513134826-f07a0146a40b/go.mod h1:FGPx0hvP73+bapzWoy5ePuhAJYgJjrFbPxgvWyortM0=
|
||||
sigs.k8s.io/kubebuilder v1.0.9-0.20200618125005-36aa113dbe99/go.mod h1:FGPx0hvP73+bapzWoy5ePuhAJYgJjrFbPxgvWyortM0=
|
||||
sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU=
|
||||
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
|
||||
sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca/go.mod h1:IIgPezJWb76P0hotTxzDbWsMYB8APh18qZnxkomBpxA=
|
||||
sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06 h1:zD2IemQ4LmOcAumeiyDWXKUI2SO0NYDe3H6QGvPOVgU=
|
||||
sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18=
|
||||
sigs.k8s.io/structured-merge-diff v1.0.2 h1:WiMoyniAVAYm03w+ImfF9IE2G23GLR/SwDnQyaNZvPk=
|
||||
sigs.k8s.io/structured-merge-diff v1.0.2/go.mod h1:IIgPezJWb76P0hotTxzDbWsMYB8APh18qZnxkomBpxA=
|
||||
sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
|
||||
sigs.k8s.io/structured-merge-diff/v3 v3.0.0 h1:dOmIZBMfhcHS09XZkMyUgkq5trg3/jRyJYFZUiaOp8E=
|
||||
sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
|
||||
|
@@ -26,6 +26,7 @@ type OnePasswordItemStatus struct {
|
||||
type OnePasswordItem struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
|
||||
Spec OnePasswordItemSpec `json:"spec,omitempty"`
|
||||
Status OnePasswordItemStatus `json:"status,omitempty"`
|
||||
|
@@ -28,7 +28,7 @@ import (
|
||||
var log = logf.Log.WithName("controller_deployment")
|
||||
var finalizer = "onepassword.com/finalizer.secret"
|
||||
|
||||
const annotationRegExpString = "^onepasswordoperator\\/[a-zA-Z\\.]+"
|
||||
const annotationRegExpString = "^operator.1password.io\\/[a-zA-Z\\.]+"
|
||||
|
||||
func Add(mgr manager.Manager, opConnectClient connect.Client) error {
|
||||
return add(mgr, newReconciler(mgr, opConnectClient))
|
||||
@@ -99,7 +99,7 @@ func (r *ReconcileDeployment) Reconcile(request reconcile.Request) (reconcile.Re
|
||||
|
||||
annotations, annotationsFound := op.GetAnnotationsForDeployment(deployment, r.opAnnotationRegExp)
|
||||
if !annotationsFound {
|
||||
reqLogger.Info("No One Password Annotations found")
|
||||
reqLogger.Info("No 1Password Annotations found")
|
||||
return reconcile.Result{}, nil
|
||||
}
|
||||
|
||||
@@ -142,7 +142,7 @@ func (r *ReconcileDeployment) cleanupKubernetesSecretForDeployment(secretName st
|
||||
if len(secretName) == 0 {
|
||||
return nil
|
||||
}
|
||||
updatedSecrets := map[string]bool{secretName: true}
|
||||
updatedSecrets := map[string]*corev1.Secret{secretName: kubernetesSecret}
|
||||
|
||||
multipleDeploymentsUsingSecret, err := r.areMultipleDeploymentsUsingSecret(updatedSecrets, *deletedDeployment)
|
||||
if err != nil {
|
||||
@@ -160,7 +160,7 @@ func (r *ReconcileDeployment) cleanupKubernetesSecretForDeployment(secretName st
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *ReconcileDeployment) areMultipleDeploymentsUsingSecret(updatedSecrets map[string]bool, deletedDeployment appsv1.Deployment) (bool, error) {
|
||||
func (r *ReconcileDeployment) areMultipleDeploymentsUsingSecret(updatedSecrets map[string]*corev1.Secret, deletedDeployment appsv1.Deployment) (bool, error) {
|
||||
deployments := &appsv1.DeploymentList{}
|
||||
opts := []client.ListOption{
|
||||
client.InNamespace(deletedDeployment.Namespace),
|
||||
@@ -191,6 +191,9 @@ func (r *ReconcileDeployment) HandleApplyingDeployment(namespace string, annotat
|
||||
reqLog := log.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name)
|
||||
|
||||
secretName := annotations[op.NameAnnotation]
|
||||
secretLabels := map[string]string(nil)
|
||||
secretType := ""
|
||||
|
||||
if len(secretName) == 0 {
|
||||
reqLog.Info("No 'item-name' annotation set. 'item-path' and 'item-name' must be set as annotations to add new secret.")
|
||||
return nil
|
||||
@@ -201,5 +204,5 @@ func (r *ReconcileDeployment) HandleApplyingDeployment(namespace string, annotat
|
||||
return fmt.Errorf("Failed to retrieve item: %v", err)
|
||||
}
|
||||
|
||||
return kubeSecrets.CreateKubernetesSecretFromItem(r.kubeClient, secretName, namespace, item)
|
||||
return kubeSecrets.CreateKubernetesSecretFromItem(r.kubeClient, secretName, namespace, item, annotations[op.RestartDeploymentsAnnotation], secretLabels, secretType, annotations)
|
||||
}
|
||||
|
@@ -258,7 +258,7 @@ var tests = []testReconcileItem{
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "Test Do not update if OnePassword Item Version has not changed",
|
||||
testName: "Test Do not update if Annotations have not changed",
|
||||
deploymentResource: &appsv1.Deployment{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: deploymentKind,
|
||||
@@ -271,6 +271,7 @@ var tests = []testReconcileItem{
|
||||
op.ItemPathAnnotation: itemPath,
|
||||
op.NameAnnotation: name,
|
||||
},
|
||||
Labels: map[string]string{},
|
||||
},
|
||||
},
|
||||
existingSecret: &corev1.Secret{
|
||||
@@ -278,7 +279,9 @@ var tests = []testReconcileItem{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
Annotations: map[string]string{
|
||||
op.VersionAnnotation: fmt.Sprint(version),
|
||||
op.VersionAnnotation: fmt.Sprint(version),
|
||||
op.ItemPathAnnotation: itemPath,
|
||||
op.NameAnnotation: name,
|
||||
},
|
||||
},
|
||||
Data: expectedSecretData,
|
||||
@@ -289,8 +292,11 @@ var tests = []testReconcileItem{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
Annotations: map[string]string{
|
||||
op.VersionAnnotation: fmt.Sprint(version),
|
||||
op.VersionAnnotation: fmt.Sprint(version),
|
||||
op.ItemPathAnnotation: itemPath,
|
||||
op.NameAnnotation: name,
|
||||
},
|
||||
Labels: map[string]string(nil),
|
||||
},
|
||||
Data: expectedSecretData,
|
||||
},
|
||||
@@ -323,6 +329,7 @@ var tests = []testReconcileItem{
|
||||
op.VersionAnnotation: "456",
|
||||
},
|
||||
},
|
||||
Type: corev1.SecretType(""),
|
||||
Data: expectedSecretData,
|
||||
},
|
||||
expectedError: nil,
|
||||
@@ -334,6 +341,7 @@ var tests = []testReconcileItem{
|
||||
op.VersionAnnotation: fmt.Sprint(version),
|
||||
},
|
||||
},
|
||||
Type: corev1.SecretType(""),
|
||||
Data: expectedSecretData,
|
||||
},
|
||||
opItem: map[string]string{
|
||||
@@ -367,6 +375,7 @@ var tests = []testReconcileItem{
|
||||
op.VersionAnnotation: fmt.Sprint(version),
|
||||
},
|
||||
},
|
||||
Type: corev1.SecretType(""),
|
||||
Data: expectedSecretData,
|
||||
},
|
||||
opItem: map[string]string{
|
||||
|
@@ -7,6 +7,7 @@ import (
|
||||
onepasswordv1 "github.com/1Password/onepassword-operator/pkg/apis/onepassword/v1"
|
||||
kubeSecrets "github.com/1Password/onepassword-operator/pkg/kubernetessecrets"
|
||||
"github.com/1Password/onepassword-operator/pkg/onepassword"
|
||||
op "github.com/1Password/onepassword-operator/pkg/onepassword"
|
||||
"github.com/1Password/onepassword-operator/pkg/utils"
|
||||
|
||||
"github.com/1Password/connect-sdk-go/connect"
|
||||
@@ -143,11 +144,15 @@ func (r *ReconcileOnePasswordItem) removeOnePasswordFinalizerFromOnePasswordItem
|
||||
|
||||
func (r *ReconcileOnePasswordItem) HandleOnePasswordItem(resource *onepasswordv1.OnePasswordItem, request reconcile.Request) error {
|
||||
secretName := resource.GetName()
|
||||
labels := resource.Labels
|
||||
annotations := resource.Annotations
|
||||
secretType := resource.Type
|
||||
autoRestart := annotations[op.RestartDeploymentsAnnotation]
|
||||
|
||||
item, err := onepassword.GetOnePasswordItemByPath(r.opConnectClient, resource.Spec.ItemPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to retrieve item: %v", err)
|
||||
}
|
||||
|
||||
return kubeSecrets.CreateKubernetesSecretFromItem(r.kubeClient, secretName, resource.Namespace, item)
|
||||
return kubeSecrets.CreateKubernetesSecretFromItem(r.kubeClient, secretName, resource.Namespace, item, autoRestart, labels, secretType, annotations)
|
||||
}
|
||||
|
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/1Password/onepassword-operator/pkg/kubernetessecrets"
|
||||
"github.com/1Password/onepassword-operator/pkg/mocks"
|
||||
op "github.com/1Password/onepassword-operator/pkg/onepassword"
|
||||
|
||||
@@ -31,6 +32,9 @@ const (
|
||||
itemId = "nwrhuano7bcwddcviubpp4mhfq"
|
||||
username = "test-user"
|
||||
password = "QmHumKc$mUeEem7caHtbaBaJ"
|
||||
firstHost = "http://localhost:8080"
|
||||
awsKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
|
||||
iceCream = "freezing blue 20%"
|
||||
userKey = "username"
|
||||
passKey = "password"
|
||||
version = 123
|
||||
@@ -116,7 +120,8 @@ var tests = []testReconcileItem{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
Annotations: map[string]string{
|
||||
op.VersionAnnotation: fmt.Sprint(version),
|
||||
op.VersionAnnotation: fmt.Sprint(version),
|
||||
op.ItemPathAnnotation: itemPath,
|
||||
},
|
||||
},
|
||||
Data: expectedSecretData,
|
||||
@@ -127,7 +132,8 @@ var tests = []testReconcileItem{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
Annotations: map[string]string{
|
||||
op.VersionAnnotation: fmt.Sprint(version),
|
||||
op.VersionAnnotation: fmt.Sprint(version),
|
||||
op.ItemPathAnnotation: itemPath,
|
||||
},
|
||||
},
|
||||
Data: expectedSecretData,
|
||||
@@ -147,6 +153,11 @@ var tests = []testReconcileItem{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
Annotations: map[string]string{
|
||||
op.VersionAnnotation: fmt.Sprint(version),
|
||||
op.ItemPathAnnotation: itemPath,
|
||||
},
|
||||
Labels: map[string]string{},
|
||||
},
|
||||
Spec: onepasswordv1.OnePasswordItemSpec{
|
||||
ItemPath: itemPath,
|
||||
@@ -157,8 +168,10 @@ var tests = []testReconcileItem{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
Annotations: map[string]string{
|
||||
op.VersionAnnotation: "456",
|
||||
op.VersionAnnotation: "456",
|
||||
op.ItemPathAnnotation: itemPath,
|
||||
},
|
||||
Labels: map[string]string{},
|
||||
},
|
||||
Data: expectedSecretData,
|
||||
},
|
||||
@@ -168,8 +181,10 @@ var tests = []testReconcileItem{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
Annotations: map[string]string{
|
||||
op.VersionAnnotation: fmt.Sprint(version),
|
||||
op.VersionAnnotation: fmt.Sprint(version),
|
||||
op.ItemPathAnnotation: itemPath,
|
||||
},
|
||||
Labels: map[string]string{},
|
||||
},
|
||||
Data: expectedSecretData,
|
||||
},
|
||||
@@ -178,6 +193,59 @@ var tests = []testReconcileItem{
|
||||
passKey: password,
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "Test Updating Type of Existing Kubernetes Secret using OnePasswordItem",
|
||||
customResource: &onepasswordv1.OnePasswordItem{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: onePasswordItemKind,
|
||||
APIVersion: onePasswordItemAPIVersion,
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
Annotations: map[string]string{
|
||||
op.VersionAnnotation: fmt.Sprint(version),
|
||||
op.ItemPathAnnotation: itemPath,
|
||||
},
|
||||
Labels: map[string]string{},
|
||||
},
|
||||
Spec: onepasswordv1.OnePasswordItemSpec{
|
||||
ItemPath: itemPath,
|
||||
},
|
||||
Type: string(corev1.SecretTypeBasicAuth),
|
||||
},
|
||||
existingSecret: &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
Annotations: map[string]string{
|
||||
op.VersionAnnotation: fmt.Sprint(version),
|
||||
op.ItemPathAnnotation: itemPath,
|
||||
},
|
||||
Labels: map[string]string{},
|
||||
},
|
||||
Type: corev1.SecretTypeBasicAuth,
|
||||
Data: expectedSecretData,
|
||||
},
|
||||
expectedError: nil,
|
||||
expectedResultSecret: &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
Annotations: map[string]string{
|
||||
op.VersionAnnotation: fmt.Sprint(version),
|
||||
op.ItemPathAnnotation: itemPath,
|
||||
},
|
||||
Labels: map[string]string{},
|
||||
},
|
||||
Type: corev1.SecretTypeBasicAuth,
|
||||
Data: expectedSecretData,
|
||||
},
|
||||
opItem: map[string]string{
|
||||
userKey: username,
|
||||
passKey: password,
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "Custom secret type",
|
||||
customResource: &onepasswordv1.OnePasswordItem{
|
||||
@@ -192,6 +260,7 @@ var tests = []testReconcileItem{
|
||||
Spec: onepasswordv1.OnePasswordItemSpec{
|
||||
ItemPath: itemPath,
|
||||
},
|
||||
Type: "custom",
|
||||
},
|
||||
existingSecret: nil,
|
||||
expectedError: nil,
|
||||
@@ -203,6 +272,7 @@ var tests = []testReconcileItem{
|
||||
op.VersionAnnotation: fmt.Sprint(version),
|
||||
},
|
||||
},
|
||||
Type: corev1.SecretType("custom"),
|
||||
Data: expectedSecretData,
|
||||
},
|
||||
opItem: map[string]string{
|
||||
@@ -210,6 +280,164 @@ var tests = []testReconcileItem{
|
||||
passKey: password,
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "Error if secret type is changed",
|
||||
customResource: &onepasswordv1.OnePasswordItem{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: onePasswordItemKind,
|
||||
APIVersion: onePasswordItemAPIVersion,
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: onepasswordv1.OnePasswordItemSpec{
|
||||
ItemPath: itemPath,
|
||||
},
|
||||
Type: "custom",
|
||||
},
|
||||
existingSecret: &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
Annotations: map[string]string{
|
||||
op.VersionAnnotation: fmt.Sprint(version),
|
||||
},
|
||||
},
|
||||
Type: corev1.SecretTypeOpaque,
|
||||
Data: expectedSecretData,
|
||||
},
|
||||
expectedError: kubernetessecrets.ErrCannotUpdateSecretType,
|
||||
expectedResultSecret: &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
Annotations: map[string]string{
|
||||
op.VersionAnnotation: fmt.Sprint(version),
|
||||
},
|
||||
},
|
||||
Type: corev1.SecretTypeOpaque,
|
||||
Data: expectedSecretData,
|
||||
},
|
||||
opItem: map[string]string{
|
||||
userKey: username,
|
||||
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,
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "Secret from 1Password item with `-`, `_` and `.`",
|
||||
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) {
|
||||
@@ -241,7 +469,10 @@ func TestReconcileOnePasswordItem(t *testing.T) {
|
||||
mocks.GetGetItemFunc = func(uuid string, vaultUUID string) (*onepassword.Item, error) {
|
||||
|
||||
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.Vault.ID = vaultUUID
|
||||
item.ID = uuid
|
||||
@@ -257,8 +488,8 @@ func TestReconcileOnePasswordItem(t *testing.T) {
|
||||
// watched resource .
|
||||
req := reconcile.Request{
|
||||
NamespacedName: types.NamespacedName{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
Name: testData.customResource.ObjectMeta.Name,
|
||||
Namespace: testData.customResource.ObjectMeta.Namespace,
|
||||
},
|
||||
}
|
||||
_, err := r.Reconcile(req)
|
||||
|
@@ -4,31 +4,60 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"reflect"
|
||||
|
||||
errs "errors"
|
||||
|
||||
"github.com/1Password/connect-sdk-go/onepassword"
|
||||
|
||||
"github.com/1Password/onepassword-operator/pkg/utils"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
kubeValidate "k8s.io/apimachinery/pkg/util/validation"
|
||||
|
||||
kubernetesClient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/log"
|
||||
)
|
||||
|
||||
const onepasswordPrefix = "onepasswordoperator"
|
||||
const NameAnnotation = onepasswordPrefix + "/item-name"
|
||||
const VersionAnnotation = onepasswordPrefix + "/item-version"
|
||||
const restartAnnotation = onepasswordPrefix + "/lastRestarted"
|
||||
const ItemPathAnnotation = onepasswordPrefix + "/item-path"
|
||||
const OnepasswordPrefix = "operator.1password.io"
|
||||
const NameAnnotation = OnepasswordPrefix + "/item-name"
|
||||
const VersionAnnotation = OnepasswordPrefix + "/item-version"
|
||||
const restartAnnotation = OnepasswordPrefix + "/last-restarted"
|
||||
const ItemPathAnnotation = OnepasswordPrefix + "/item-path"
|
||||
const RestartDeploymentsAnnotation = OnepasswordPrefix + "/auto-restart"
|
||||
|
||||
var ErrCannotUpdateSecretType = errs.New("Cannot change secret type. Secret type is immutable")
|
||||
|
||||
var log = logf.Log
|
||||
|
||||
func CreateKubernetesSecretFromItem(kubeClient kubernetesClient.Client, secretName, namespace string, item *onepassword.Item) error {
|
||||
func CreateKubernetesSecretFromItem(kubeClient kubernetesClient.Client, secretName, namespace string, item *onepassword.Item, autoRestart string, labels map[string]string, secretType string, secretAnnotations map[string]string) error {
|
||||
|
||||
itemVersion := fmt.Sprint(item.Version)
|
||||
annotations := map[string]string{
|
||||
VersionAnnotation: itemVersion,
|
||||
ItemPathAnnotation: fmt.Sprintf("vaults/%v/items/%v", item.Vault.ID, item.ID),
|
||||
|
||||
// If secretAnnotations is nil we create an empty map so we can later assign values for the OP Annotations in the map
|
||||
if secretAnnotations == nil {
|
||||
secretAnnotations = map[string]string{}
|
||||
}
|
||||
secret := BuildKubernetesSecretFromOnePasswordItem(secretName, namespace, annotations, *item)
|
||||
|
||||
secretAnnotations[VersionAnnotation] = itemVersion
|
||||
secretAnnotations[ItemPathAnnotation] = fmt.Sprintf("vaults/%v/items/%v", item.Vault.ID, item.ID)
|
||||
|
||||
if autoRestart != "" {
|
||||
_, err := utils.StringToBool(autoRestart)
|
||||
if err != nil {
|
||||
log.Error(err, "Error parsing %v annotation on Secret %v. Must be true or false. Defaulting to false.", RestartDeploymentsAnnotation, secretName)
|
||||
return err
|
||||
}
|
||||
secretAnnotations[RestartDeploymentsAnnotation] = autoRestart
|
||||
}
|
||||
|
||||
// "Opaque" and "" secret types are treated the same by Kubernetes.
|
||||
secret := BuildKubernetesSecretFromOnePasswordItem(secretName, namespace, secretAnnotations, labels, secretType, *item)
|
||||
|
||||
currentSecret := &corev1.Secret{}
|
||||
err := kubeClient.Get(context.Background(), types.NamespacedName{Name: secret.Name, Namespace: secret.Namespace}, currentSecret)
|
||||
@@ -39,9 +68,17 @@ func CreateKubernetesSecretFromItem(kubeClient kubernetesClient.Client, secretNa
|
||||
return err
|
||||
}
|
||||
|
||||
if currentSecret.Annotations[VersionAnnotation] != itemVersion {
|
||||
currentAnnotations := currentSecret.Annotations
|
||||
currentLabels := currentSecret.Labels
|
||||
currentSecretType := string(currentSecret.Type)
|
||||
if !reflect.DeepEqual(currentSecretType, secretType) {
|
||||
return ErrCannotUpdateSecretType
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(currentAnnotations, secretAnnotations) || !reflect.DeepEqual(currentLabels, labels) {
|
||||
log.Info(fmt.Sprintf("Updating Secret %v at namespace '%v'", secret.Name, secret.Namespace))
|
||||
currentSecret.ObjectMeta.Annotations = annotations
|
||||
currentSecret.ObjectMeta.Annotations = secretAnnotations
|
||||
currentSecret.ObjectMeta.Labels = labels
|
||||
currentSecret.Data = secret.Data
|
||||
return kubeClient.Update(context.Background(), currentSecret)
|
||||
}
|
||||
@@ -50,23 +87,93 @@ func CreateKubernetesSecretFromItem(kubeClient kubernetesClient.Client, secretNa
|
||||
return nil
|
||||
}
|
||||
|
||||
func BuildKubernetesSecretFromOnePasswordItem(name, namespace string, annotations map[string]string, item onepassword.Item) *corev1.Secret {
|
||||
func BuildKubernetesSecretFromOnePasswordItem(name, namespace string, annotations map[string]string, labels map[string]string, secretType string, item onepassword.Item) *corev1.Secret {
|
||||
return &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Name: formatSecretName(name),
|
||||
Namespace: namespace,
|
||||
Annotations: annotations,
|
||||
Labels: labels,
|
||||
},
|
||||
Data: BuildKubernetesSecretData(item.Fields),
|
||||
Data: BuildKubernetesSecretData(item.Fields, item.Files),
|
||||
Type: corev1.SecretType(secretType),
|
||||
}
|
||||
}
|
||||
|
||||
func BuildKubernetesSecretData(fields []*onepassword.ItemField) map[string][]byte {
|
||||
func BuildKubernetesSecretData(fields []*onepassword.ItemField, files []*onepassword.File) map[string][]byte {
|
||||
secretData := map[string][]byte{}
|
||||
for i := 0; i < len(fields); i++ {
|
||||
if fields[i].Value != "" {
|
||||
secretData[fields[i].Label] = []byte(fields[i].Value)
|
||||
key := formatSecretDataName(fields[i].Label)
|
||||
secretData[key] = []byte(fields[i].Value)
|
||||
}
|
||||
}
|
||||
|
||||
// populate unpopulated fields from files
|
||||
for _, file := range files {
|
||||
content, err := file.Content()
|
||||
if err != nil {
|
||||
log.Error(err, "Could not load contents of file %s", file.Name)
|
||||
continue
|
||||
}
|
||||
if content != nil {
|
||||
key := file.Name
|
||||
if secretData[key] == nil {
|
||||
secretData[key] = content
|
||||
} else {
|
||||
log.Info(fmt.Sprintf("File '%s' ignored because of a field with the same name", file.Name))
|
||||
}
|
||||
}
|
||||
}
|
||||
return secretData
|
||||
}
|
||||
|
||||
// formatSecretName rewrites a value to be a valid Secret name.
|
||||
//
|
||||
// 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)
|
||||
}
|
||||
|
||||
// formatSecretDataName rewrites a value to be a valid Secret data key.
|
||||
//
|
||||
// The Secret data keys must consist of alphanumeric numbers, `-`, `_` or `.`
|
||||
// (https://kubernetes.io/docs/concepts/configuration/secret/#overview-of-secrets)
|
||||
func formatSecretDataName(value string) string {
|
||||
if errs := kubeValidate.IsConfigMapKey(value); len(errs) == 0 {
|
||||
return value
|
||||
}
|
||||
return createValidSecretDataName(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, "-.")
|
||||
}
|
||||
|
||||
var invalidDataChars = regexp.MustCompile("[^a-zA-Z0-9-._]+")
|
||||
var invalidStartEndChars = regexp.MustCompile("(^[^a-zA-Z0-9-._]+|[^a-zA-Z0-9-._]+$)")
|
||||
|
||||
func createValidSecretDataName(value string) string {
|
||||
result := invalidStartEndChars.ReplaceAllString(value, "")
|
||||
result = invalidDataChars.ReplaceAllString(result, "-")
|
||||
|
||||
if len(result) > kubeValidate.DNS1123SubdomainMaxLength {
|
||||
result = result[0:kubeValidate.DNS1123SubdomainMaxLength]
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
@@ -9,10 +9,13 @@ import (
|
||||
"github.com/1Password/connect-sdk-go/onepassword"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
kubeValidate "k8s.io/apimachinery/pkg/util/validation"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
)
|
||||
|
||||
const restartDeploymentAnnotation = "false"
|
||||
|
||||
type k8s struct {
|
||||
clientset kubernetes.Interface
|
||||
}
|
||||
@@ -28,7 +31,13 @@ func TestCreateKubernetesSecretFromOnePasswordItem(t *testing.T) {
|
||||
item.ID = "h46bb3jddvay7nxopfhvlwg35q"
|
||||
|
||||
kubeClient := fake.NewFakeClient()
|
||||
err := CreateKubernetesSecretFromItem(kubeClient, secretName, namespace, &item)
|
||||
secretLabels := map[string]string{}
|
||||
secretAnnotations := map[string]string{
|
||||
"testAnnotation": "exists",
|
||||
}
|
||||
secretType := ""
|
||||
|
||||
err := CreateKubernetesSecretFromItem(kubeClient, secretName, namespace, &item, restartDeploymentAnnotation, secretLabels, secretType, secretAnnotations)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
@@ -40,6 +49,10 @@ func TestCreateKubernetesSecretFromOnePasswordItem(t *testing.T) {
|
||||
}
|
||||
compareFields(item.Fields, createdSecret.Data, t)
|
||||
compareAnnotationsToItem(createdSecret.Annotations, item, t)
|
||||
|
||||
if createdSecret.Annotations["testAnnotation"] != "exists" {
|
||||
t.Errorf("Expected testAnnotation to be merged with existing annotations, but wasn't.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateKubernetesSecretFromOnePasswordItem(t *testing.T) {
|
||||
@@ -53,7 +66,12 @@ func TestUpdateKubernetesSecretFromOnePasswordItem(t *testing.T) {
|
||||
item.ID = "h46bb3jddvay7nxopfhvlwg35q"
|
||||
|
||||
kubeClient := fake.NewFakeClient()
|
||||
err := CreateKubernetesSecretFromItem(kubeClient, secretName, namespace, &item)
|
||||
secretLabels := map[string]string{}
|
||||
secretAnnotations := map[string]string{}
|
||||
secretType := ""
|
||||
|
||||
err := CreateKubernetesSecretFromItem(kubeClient, secretName, namespace, &item, restartDeploymentAnnotation, secretLabels, secretType, secretAnnotations)
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
@@ -64,7 +82,7 @@ func TestUpdateKubernetesSecretFromOnePasswordItem(t *testing.T) {
|
||||
newItem.Version = 456
|
||||
newItem.Vault.ID = "hfnjvi6aymbsnfc2xeeoheizda"
|
||||
newItem.ID = "h46bb3jddvay7nxopfhvlwg35q"
|
||||
err = CreateKubernetesSecretFromItem(kubeClient, secretName, namespace, &newItem)
|
||||
err = CreateKubernetesSecretFromItem(kubeClient, secretName, namespace, &newItem, restartDeploymentAnnotation, secretLabels, secretType, secretAnnotations)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
@@ -80,7 +98,7 @@ func TestUpdateKubernetesSecretFromOnePasswordItem(t *testing.T) {
|
||||
func TestBuildKubernetesSecretData(t *testing.T) {
|
||||
fields := generateFields(5)
|
||||
|
||||
secretData := BuildKubernetesSecretData(fields)
|
||||
secretData := BuildKubernetesSecretData(fields, nil)
|
||||
if len(secretData) != len(fields) {
|
||||
t.Errorf("Unexpected number of secret fields returned. Expected 3, got %v", len(secretData))
|
||||
}
|
||||
@@ -97,9 +115,11 @@ func TestBuildKubernetesSecretFromOnePasswordItem(t *testing.T) {
|
||||
}
|
||||
item := onepassword.Item{}
|
||||
item.Fields = generateFields(5)
|
||||
labels := map[string]string{}
|
||||
secretType := ""
|
||||
|
||||
kubeSecret := BuildKubernetesSecretFromOnePasswordItem(name, namespace, annotations, item)
|
||||
if kubeSecret.Name != name {
|
||||
kubeSecret := BuildKubernetesSecretFromOnePasswordItem(name, namespace, annotations, labels, secretType, item)
|
||||
if kubeSecret.Name != strings.ToLower(name) {
|
||||
t.Errorf("Expected name value: %v but got: %v", name, kubeSecret.Name)
|
||||
}
|
||||
if kubeSecret.Namespace != namespace {
|
||||
@@ -111,6 +131,79 @@ func TestBuildKubernetesSecretFromOnePasswordItem(t *testing.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",
|
||||
}
|
||||
labels := map[string]string{}
|
||||
item := onepassword.Item{}
|
||||
secretType := ""
|
||||
|
||||
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, labels, secretType, 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 TestCreateKubernetesTLSSecretFromOnePasswordItem(t *testing.T) {
|
||||
secretName := "tls-test-secret-name"
|
||||
namespace := "test"
|
||||
|
||||
item := onepassword.Item{}
|
||||
item.Fields = generateFields(5)
|
||||
item.Version = 123
|
||||
item.Vault.ID = "hfnjvi6aymbsnfc2xeeoheizda"
|
||||
item.ID = "h46bb3jddvay7nxopfhvlwg35q"
|
||||
|
||||
kubeClient := fake.NewFakeClient()
|
||||
secretLabels := map[string]string{}
|
||||
secretAnnotations := map[string]string{
|
||||
"testAnnotation": "exists",
|
||||
}
|
||||
secretType := "kubernetes.io/tls"
|
||||
|
||||
err := CreateKubernetesSecretFromItem(kubeClient, secretName, namespace, &item, restartDeploymentAnnotation, secretLabels, secretType, secretAnnotations)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
createdSecret := &corev1.Secret{}
|
||||
err = kubeClient.Get(context.Background(), types.NamespacedName{Name: secretName, Namespace: namespace}, createdSecret)
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("Secret was not created: %v", err)
|
||||
}
|
||||
|
||||
if createdSecret.Type != corev1.SecretTypeTLS {
|
||||
t.Errorf("Expected secretType to be of tyype corev1.SecretTypeTLS, got %s", string(createdSecret.Type))
|
||||
}
|
||||
}
|
||||
|
||||
func compareAnnotationsToItem(annotations map[string]string, item onepassword.Item, t *testing.T) {
|
||||
actualVaultId, actualItemId, err := ParseVaultIdAndItemIdFromPath(annotations[ItemPathAnnotation])
|
||||
if err != nil {
|
||||
@@ -125,6 +218,10 @@ func compareAnnotationsToItem(annotations map[string]string, item onepassword.It
|
||||
if annotations[VersionAnnotation] != fmt.Sprint(item.Version) {
|
||||
t.Errorf("Expected annotation version to be %v but was %v", item.Version, annotations[VersionAnnotation])
|
||||
}
|
||||
|
||||
if annotations[RestartDeploymentsAnnotation] != "false" {
|
||||
t.Errorf("Expected restart deployments annotation to be %v but was %v", restartDeploymentAnnotation, RestartDeploymentsAnnotation)
|
||||
}
|
||||
}
|
||||
|
||||
func compareFields(actualFields []*onepassword.ItemField, secretData map[string][]byte, t *testing.T) {
|
||||
@@ -158,3 +255,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)
|
||||
}
|
||||
|
||||
func validLabel(v string) bool {
|
||||
if err := kubeValidate.IsConfigMapKey(v); len(err) > 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
@@ -7,6 +7,7 @@ import (
|
||||
type TestClient struct {
|
||||
GetVaultsFunc func() ([]onepassword.Vault, error)
|
||||
GetVaultsByTitleFunc func(title string) ([]onepassword.Vault, error)
|
||||
GetVaultFunc func(uuid string) (*onepassword.Vault, error)
|
||||
GetItemFunc func(uuid string, vaultUUID string) (*onepassword.Item, error)
|
||||
GetItemsFunc func(vaultUUID string) ([]onepassword.Item, error)
|
||||
GetItemsByTitleFunc func(title string, vaultUUID string) ([]onepassword.Item, error)
|
||||
@@ -14,11 +15,14 @@ type TestClient struct {
|
||||
CreateItemFunc func(item *onepassword.Item, vaultUUID string) (*onepassword.Item, error)
|
||||
UpdateItemFunc func(item *onepassword.Item, vaultUUID string) (*onepassword.Item, error)
|
||||
DeleteItemFunc func(item *onepassword.Item, vaultUUID string) error
|
||||
GetFileFunc func(uuid string, itemUUID string, vaultUUID string) (*onepassword.File, error)
|
||||
GetFileContentFunc func(file *onepassword.File) ([]byte, error)
|
||||
}
|
||||
|
||||
var (
|
||||
GetGetVaultsFunc func() ([]onepassword.Vault, error)
|
||||
DoGetVaultsByTitleFunc func(title string) ([]onepassword.Vault, error)
|
||||
DoGetVaultFunc func(uuid string) (*onepassword.Vault, error)
|
||||
GetGetItemFunc func(uuid string, vaultUUID string) (*onepassword.Item, error)
|
||||
DoGetItemsByTitleFunc func(title string, vaultUUID string) ([]onepassword.Item, error)
|
||||
DoGetItemByTitleFunc func(title string, vaultUUID string) (*onepassword.Item, error)
|
||||
@@ -26,6 +30,8 @@ var (
|
||||
DoDeleteItemFunc func(item *onepassword.Item, vaultUUID string) error
|
||||
DoGetItemsFunc func(vaultUUID string) ([]onepassword.Item, error)
|
||||
DoUpdateItemFunc func(item *onepassword.Item, vaultUUID string) (*onepassword.Item, error)
|
||||
DoGetFileFunc func(uuid string, itemUUID string, vaultUUID string) (*onepassword.File, error)
|
||||
DoGetFileContentFunc func(file *onepassword.File) ([]byte, error)
|
||||
)
|
||||
|
||||
// Do is the mock client's `Do` func
|
||||
@@ -37,6 +43,10 @@ func (m *TestClient) GetVaultsByTitle(title string) ([]onepassword.Vault, error)
|
||||
return DoGetVaultsByTitleFunc(title)
|
||||
}
|
||||
|
||||
func (m *TestClient) GetVault(uuid string) (*onepassword.Vault, error) {
|
||||
return DoGetVaultFunc(uuid)
|
||||
}
|
||||
|
||||
func (m *TestClient) GetItem(uuid string, vaultUUID string) (*onepassword.Item, error) {
|
||||
return GetGetItemFunc(uuid, vaultUUID)
|
||||
}
|
||||
@@ -64,3 +74,11 @@ func (m *TestClient) DeleteItem(item *onepassword.Item, vaultUUID string) error
|
||||
func (m *TestClient) UpdateItem(item *onepassword.Item, vaultUUID string) (*onepassword.Item, error) {
|
||||
return DoUpdateItemFunc(item, vaultUUID)
|
||||
}
|
||||
|
||||
func (m *TestClient) GetFile(uuid string, itemUUID string, vaultUUID string) (*onepassword.File, error) {
|
||||
return DoGetFileFunc(uuid, itemUUID, vaultUUID)
|
||||
}
|
||||
|
||||
func (m *TestClient) GetFileContent(file *onepassword.File) ([]byte, error) {
|
||||
return DoGetFileContentFunc(file)
|
||||
}
|
||||
|
@@ -4,14 +4,16 @@ import (
|
||||
"regexp"
|
||||
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
OnepasswordPrefix = "onepasswordoperator"
|
||||
ItemPathAnnotation = OnepasswordPrefix + "/item-path"
|
||||
NameAnnotation = OnepasswordPrefix + "/item-name"
|
||||
VersionAnnotation = OnepasswordPrefix + "/item-version"
|
||||
RestartAnnotation = OnepasswordPrefix + "/lastRestarted"
|
||||
OnepasswordPrefix = "operator.1password.io"
|
||||
ItemPathAnnotation = OnepasswordPrefix + "/item-path"
|
||||
NameAnnotation = OnepasswordPrefix + "/item-name"
|
||||
VersionAnnotation = OnepasswordPrefix + "/item-version"
|
||||
RestartAnnotation = OnepasswordPrefix + "/last-restarted"
|
||||
RestartDeploymentsAnnotation = OnepasswordPrefix + "/auto-restart"
|
||||
)
|
||||
|
||||
func GetAnnotationsForDeployment(deployment *appsv1.Deployment, regex *regexp.Regexp) (map[string]string, bool) {
|
||||
@@ -34,17 +36,25 @@ func GetAnnotationsForDeployment(deployment *appsv1.Deployment, regex *regexp.Re
|
||||
func FilterAnnotations(annotations map[string]string, regex *regexp.Regexp) map[string]string {
|
||||
filteredAnnotations := make(map[string]string)
|
||||
for key, value := range annotations {
|
||||
if regex.MatchString(key) {
|
||||
if regex.MatchString(key) && key != RestartAnnotation && key != RestartDeploymentsAnnotation {
|
||||
filteredAnnotations[key] = value
|
||||
}
|
||||
}
|
||||
return filteredAnnotations
|
||||
}
|
||||
|
||||
func AreAnnotationsUsingSecrets(annotations map[string]string, secrets map[string]bool) bool {
|
||||
func AreAnnotationsUsingSecrets(annotations map[string]string, secrets map[string]*corev1.Secret) bool {
|
||||
_, ok := secrets[annotations[NameAnnotation]]
|
||||
if ok {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func AppendAnnotationUpdatedSecret(annotations map[string]string, secrets map[string]*corev1.Secret, updatedDeploymentSecrets map[string]*corev1.Secret) map[string]*corev1.Secret {
|
||||
secret, ok := secrets[annotations[NameAnnotation]]
|
||||
if ok {
|
||||
updatedDeploymentSecrets[secret.Name] = secret
|
||||
}
|
||||
return updatedDeploymentSecrets
|
||||
}
|
||||
|
@@ -7,7 +7,7 @@ import (
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
)
|
||||
|
||||
const AnnotationRegExpString = "^onepasswordoperator\\/[a-zA-Z\\.]+"
|
||||
const AnnotationRegExpString = "^operator.1password.io\\/[a-zA-Z\\.]+"
|
||||
|
||||
func TestFilterAnnotations(t *testing.T) {
|
||||
invalidAnnotation1 := "onepasswordconnect/vaultId"
|
||||
|
117
pkg/onepassword/connect_setup.go
Normal file
117
pkg/onepassword/connect_setup.go
Normal file
@@ -0,0 +1,117 @@
|
||||
package onepassword
|
||||
|
||||
import (
|
||||
"context"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"os"
|
||||
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
errors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/yaml"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/log"
|
||||
)
|
||||
|
||||
var logConnectSetup = logf.Log.WithName("ConnectSetup")
|
||||
var deploymentPath = "deploy/connect/deployment.yaml"
|
||||
var servicePath = "deploy/connect/service.yaml"
|
||||
|
||||
func SetupConnect(kubeClient client.Client, deploymentNamespace string) error {
|
||||
err := setupService(kubeClient, servicePath, deploymentNamespace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = setupDeployment(kubeClient, deploymentPath, deploymentNamespace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func setupDeployment(kubeClient client.Client, deploymentPath string, deploymentNamespace string) error {
|
||||
existingDeployment := &appsv1.Deployment{}
|
||||
|
||||
// check if deployment has already been created
|
||||
err := kubeClient.Get(context.Background(), types.NamespacedName{Name: "onepassword-connect", Namespace: deploymentNamespace}, existingDeployment)
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
logConnectSetup.Info("No existing Connect deployment found. Creating Deployment")
|
||||
return createDeployment(kubeClient, deploymentPath, deploymentNamespace)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func createDeployment(kubeClient client.Client, deploymentPath string, deploymentNamespace string) error {
|
||||
deployment, err := getDeploymentToCreate(deploymentPath, deploymentNamespace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = kubeClient.Create(context.Background(), deployment)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getDeploymentToCreate(deploymentPath string, deploymentNamespace string) (*appsv1.Deployment, error) {
|
||||
f, err := os.Open(deploymentPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
deployment := &appsv1.Deployment{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Namespace: deploymentNamespace,
|
||||
},
|
||||
}
|
||||
|
||||
err = yaml.NewYAMLOrJSONDecoder(f, 4096).Decode(deployment)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return deployment, nil
|
||||
}
|
||||
|
||||
func setupService(kubeClient client.Client, servicePath string, deploymentNamespace string) error {
|
||||
existingService := &corev1.Service{}
|
||||
|
||||
//check if service has already been created
|
||||
err := kubeClient.Get(context.Background(), types.NamespacedName{Name: "onepassword-connect", Namespace: deploymentNamespace}, existingService)
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
logConnectSetup.Info("No existing Connect service found. Creating Service")
|
||||
return createService(kubeClient, servicePath, deploymentNamespace)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func createService(kubeClient client.Client, servicePath string, deploymentNamespace string) error {
|
||||
f, err := os.Open(servicePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
service := &corev1.Service{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Namespace: deploymentNamespace,
|
||||
},
|
||||
}
|
||||
|
||||
err = yaml.NewYAMLOrJSONDecoder(f, 4096).Decode(service)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = kubeClient.Create(context.Background(), service)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
65
pkg/onepassword/connect_setup_test.go
Normal file
65
pkg/onepassword/connect_setup_test.go
Normal file
@@ -0,0 +1,65 @@
|
||||
package onepassword
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/kubectl/pkg/scheme"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
)
|
||||
|
||||
var defaultNamespacedName = types.NamespacedName{Name: "onepassword-connect", Namespace: "default"}
|
||||
|
||||
func TestServiceSetup(t *testing.T) {
|
||||
|
||||
// Register operator types with the runtime scheme.
|
||||
s := scheme.Scheme
|
||||
|
||||
// Objects to track in the fake client.
|
||||
objs := []runtime.Object{}
|
||||
|
||||
// Create a fake client to mock API calls.
|
||||
client := fake.NewFakeClientWithScheme(s, objs...)
|
||||
|
||||
err := setupService(client, "../../deploy/connect/service.yaml", defaultNamespacedName.Namespace)
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("Error Setting Up Connect: %v", err)
|
||||
}
|
||||
|
||||
// check that service was created
|
||||
service := &corev1.Service{}
|
||||
err = client.Get(context.TODO(), defaultNamespacedName, service)
|
||||
if err != nil {
|
||||
t.Errorf("Error Setting Up Connect service: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeploymentSetup(t *testing.T) {
|
||||
|
||||
// Register operator types with the runtime scheme.
|
||||
s := scheme.Scheme
|
||||
|
||||
// Objects to track in the fake client.
|
||||
objs := []runtime.Object{}
|
||||
|
||||
// Create a fake client to mock API calls.
|
||||
client := fake.NewFakeClientWithScheme(s, objs...)
|
||||
|
||||
err := setupDeployment(client, "../../deploy/connect/deployment.yaml", defaultNamespacedName.Namespace)
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("Error Setting Up Connect: %v", err)
|
||||
}
|
||||
|
||||
// check that deployment was created
|
||||
deployment := &appsv1.Deployment{}
|
||||
err = client.Get(context.TODO(), defaultNamespacedName, deployment)
|
||||
if err != nil {
|
||||
t.Errorf("Error Setting Up Connect deployment: %v", err)
|
||||
}
|
||||
}
|
@@ -1,8 +1,10 @@
|
||||
package onepassword
|
||||
|
||||
import corev1 "k8s.io/api/core/v1"
|
||||
import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
func AreContainersUsingSecrets(containers []corev1.Container, secrets map[string]bool) bool {
|
||||
func AreContainersUsingSecrets(containers []corev1.Container, secrets map[string]*corev1.Secret) bool {
|
||||
for i := 0; i < len(containers); i++ {
|
||||
envVariables := containers[i].Env
|
||||
for j := 0; j < len(envVariables); j++ {
|
||||
@@ -13,6 +15,39 @@ func AreContainersUsingSecrets(containers []corev1.Container, secrets map[string
|
||||
}
|
||||
}
|
||||
}
|
||||
envFromVariables := containers[i].EnvFrom
|
||||
for j := 0; j < len(envFromVariables); j++ {
|
||||
if envFromVariables[j].SecretRef != nil {
|
||||
_, ok := secrets[envFromVariables[j].SecretRef.Name]
|
||||
if ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func AppendUpdatedContainerSecrets(containers []corev1.Container, secrets map[string]*corev1.Secret, updatedDeploymentSecrets map[string]*corev1.Secret) map[string]*corev1.Secret {
|
||||
for i := 0; i < len(containers); i++ {
|
||||
envVariables := containers[i].Env
|
||||
for j := 0; j < len(envVariables); j++ {
|
||||
if envVariables[j].ValueFrom != nil && envVariables[j].ValueFrom.SecretKeyRef != nil {
|
||||
secret, ok := secrets[envVariables[j].ValueFrom.SecretKeyRef.Name]
|
||||
if ok {
|
||||
updatedDeploymentSecrets[secret.Name] = secret
|
||||
}
|
||||
}
|
||||
}
|
||||
envFromVariables := containers[i].EnvFrom
|
||||
for j := 0; j < len(envFromVariables); j++ {
|
||||
if envFromVariables[j].SecretRef != nil {
|
||||
secret, ok := secrets[envFromVariables[j].SecretRef.LocalObjectReference.Name]
|
||||
if ok {
|
||||
updatedDeploymentSecrets[secret.Name] = secret
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return updatedDeploymentSecrets
|
||||
}
|
||||
|
@@ -2,12 +2,15 @@ package onepassword
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func TestAreContainersUsingSecrets(t *testing.T) {
|
||||
secretNamesToSearch := map[string]bool{
|
||||
"onepassword-database-secret": true,
|
||||
"onepassword-api-key": true,
|
||||
func TestAreContainersUsingSecretsFromEnv(t *testing.T) {
|
||||
secretNamesToSearch := map[string]*corev1.Secret{
|
||||
"onepassword-database-secret": &corev1.Secret{},
|
||||
"onepassword-api-key": &corev1.Secret{},
|
||||
}
|
||||
|
||||
containerSecretNames := []string{
|
||||
@@ -16,7 +19,26 @@ func TestAreContainersUsingSecrets(t *testing.T) {
|
||||
"some_other_key",
|
||||
}
|
||||
|
||||
containers := generateContainers(containerSecretNames)
|
||||
containers := generateContainersWithSecretRefsFromEnv(containerSecretNames)
|
||||
|
||||
if !AreContainersUsingSecrets(containers, secretNamesToSearch) {
|
||||
t.Errorf("Expected that containers were using secrets but they were not detected.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAreContainersUsingSecretsFromEnvFrom(t *testing.T) {
|
||||
secretNamesToSearch := map[string]*corev1.Secret{
|
||||
"onepassword-database-secret": {},
|
||||
"onepassword-api-key": {},
|
||||
}
|
||||
|
||||
containerSecretNames := []string{
|
||||
"onepassword-database-secret",
|
||||
"onepassword-api-key",
|
||||
"some_other_key",
|
||||
}
|
||||
|
||||
containers := generateContainersWithSecretRefsFromEnvFrom(containerSecretNames)
|
||||
|
||||
if !AreContainersUsingSecrets(containers, secretNamesToSearch) {
|
||||
t.Errorf("Expected that containers were using secrets but they were not detected.")
|
||||
@@ -24,18 +46,40 @@ func TestAreContainersUsingSecrets(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAreContainersNotUsingSecrets(t *testing.T) {
|
||||
secretNamesToSearch := map[string]bool{
|
||||
"onepassword-database-secret": true,
|
||||
"onepassword-api-key": true,
|
||||
secretNamesToSearch := map[string]*corev1.Secret{
|
||||
"onepassword-database-secret": {},
|
||||
"onepassword-api-key": {},
|
||||
}
|
||||
|
||||
containerSecretNames := []string{
|
||||
"some_other_key",
|
||||
}
|
||||
|
||||
containers := generateContainers(containerSecretNames)
|
||||
containers := generateContainersWithSecretRefsFromEnv(containerSecretNames)
|
||||
|
||||
if AreContainersUsingSecrets(containers, secretNamesToSearch) {
|
||||
t.Errorf("Expected that containers were not using secrets but they were detected.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppendUpdatedContainerSecretsParsesEnvFromEnv(t *testing.T) {
|
||||
secretNamesToSearch := map[string]*corev1.Secret{
|
||||
"onepassword-database-secret": {},
|
||||
"onepassword-api-key": {ObjectMeta: metav1.ObjectMeta{Name: "onepassword-api-key"}},
|
||||
}
|
||||
|
||||
containerSecretNames := []string{
|
||||
"onepassword-api-key",
|
||||
}
|
||||
|
||||
containers := generateContainersWithSecretRefsFromEnvFrom(containerSecretNames)
|
||||
|
||||
updatedDeploymentSecrets := map[string]*corev1.Secret{}
|
||||
updatedDeploymentSecrets = AppendUpdatedContainerSecrets(containers, secretNamesToSearch, updatedDeploymentSecrets)
|
||||
|
||||
secretKeyName := "onepassword-api-key"
|
||||
|
||||
if updatedDeploymentSecrets[secretKeyName] != secretNamesToSearch[secretKeyName] {
|
||||
t.Errorf("Expected that updated Secret from envfrom is found.")
|
||||
}
|
||||
}
|
||||
|
@@ -1,10 +1,26 @@
|
||||
package onepassword
|
||||
|
||||
import appsv1 "k8s.io/api/apps/v1"
|
||||
import (
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
func IsDeploymentUsingSecrets(deployment *appsv1.Deployment, secrets map[string]bool) bool {
|
||||
func IsDeploymentUsingSecrets(deployment *appsv1.Deployment, secrets map[string]*corev1.Secret) bool {
|
||||
volumes := deployment.Spec.Template.Spec.Volumes
|
||||
containers := deployment.Spec.Template.Spec.Containers
|
||||
containers = append(containers, deployment.Spec.Template.Spec.InitContainers...)
|
||||
return AreAnnotationsUsingSecrets(deployment.Annotations, secrets) || AreContainersUsingSecrets(containers, secrets) || AreVolumesUsingSecrets(volumes, secrets)
|
||||
}
|
||||
|
||||
func GetUpdatedSecretsForDeployment(deployment *appsv1.Deployment, secrets map[string]*corev1.Secret) map[string]*corev1.Secret {
|
||||
volumes := deployment.Spec.Template.Spec.Volumes
|
||||
containers := deployment.Spec.Template.Spec.Containers
|
||||
containers = append(containers, deployment.Spec.Template.Spec.InitContainers...)
|
||||
|
||||
updatedSecretsForDeployment := map[string]*corev1.Secret{}
|
||||
AppendAnnotationUpdatedSecret(deployment.Annotations, secrets, updatedSecretsForDeployment)
|
||||
AppendUpdatedContainerSecrets(containers, secrets, updatedSecretsForDeployment)
|
||||
AppendUpdatedVolumeSecrets(volumes, secrets, updatedSecretsForDeployment)
|
||||
|
||||
return updatedSecretsForDeployment
|
||||
}
|
||||
|
@@ -4,12 +4,13 @@ import (
|
||||
"testing"
|
||||
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
func TestIsDeploymentUsingSecretsUsingVolumes(t *testing.T) {
|
||||
secretNamesToSearch := map[string]bool{
|
||||
"onepassword-database-secret": true,
|
||||
"onepassword-api-key": true,
|
||||
secretNamesToSearch := map[string]*corev1.Secret{
|
||||
"onepassword-database-secret": &corev1.Secret{},
|
||||
"onepassword-api-key": &corev1.Secret{},
|
||||
}
|
||||
|
||||
volumeSecretNames := []string{
|
||||
@@ -26,9 +27,9 @@ func TestIsDeploymentUsingSecretsUsingVolumes(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIsDeploymentUsingSecretsUsingContainers(t *testing.T) {
|
||||
secretNamesToSearch := map[string]bool{
|
||||
"onepassword-database-secret": true,
|
||||
"onepassword-api-key": true,
|
||||
secretNamesToSearch := map[string]*corev1.Secret{
|
||||
"onepassword-database-secret": &corev1.Secret{},
|
||||
"onepassword-api-key": &corev1.Secret{},
|
||||
}
|
||||
|
||||
containerSecretNames := []string{
|
||||
@@ -38,16 +39,16 @@ func TestIsDeploymentUsingSecretsUsingContainers(t *testing.T) {
|
||||
}
|
||||
|
||||
deployment := &appsv1.Deployment{}
|
||||
deployment.Spec.Template.Spec.Containers = generateContainers(containerSecretNames)
|
||||
deployment.Spec.Template.Spec.Containers = generateContainersWithSecretRefsFromEnv(containerSecretNames)
|
||||
if !IsDeploymentUsingSecrets(deployment, secretNamesToSearch) {
|
||||
t.Errorf("Expected that deployment was using secrets but they were not detected.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsDeploymentNotUSingSecrets(t *testing.T) {
|
||||
secretNamesToSearch := map[string]bool{
|
||||
"onepassword-database-secret": true,
|
||||
"onepassword-api-key": true,
|
||||
secretNamesToSearch := map[string]*corev1.Secret{
|
||||
"onepassword-database-secret": &corev1.Secret{},
|
||||
"onepassword-api-key": &corev1.Secret{},
|
||||
}
|
||||
|
||||
deployment := &appsv1.Deployment{}
|
||||
|
@@ -30,6 +30,14 @@ func GetOnePasswordItemByPath(opConnectClient connect.Client, path string) (*one
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, file := range item.Files {
|
||||
_, err := opConnectClient.GetFileContent(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return item, nil
|
||||
}
|
||||
|
||||
|
@@ -17,8 +17,7 @@ func generateVolumes(names []string) []corev1.Volume {
|
||||
}
|
||||
return volumes
|
||||
}
|
||||
|
||||
func generateContainers(names []string) []corev1.Container {
|
||||
func generateContainersWithSecretRefsFromEnv(names []string) []corev1.Container {
|
||||
containers := []corev1.Container{}
|
||||
for i := 0; i < len(names); i++ {
|
||||
container := corev1.Container{
|
||||
@@ -40,3 +39,16 @@ func generateContainers(names []string) []corev1.Container {
|
||||
}
|
||||
return containers
|
||||
}
|
||||
|
||||
func generateContainersWithSecretRefsFromEnvFrom(names []string) []corev1.Container {
|
||||
containers := []corev1.Container{}
|
||||
for i := 0; i < len(names); i++ {
|
||||
container := corev1.Container{
|
||||
EnvFrom: []corev1.EnvFromSource{
|
||||
{SecretRef: &corev1.SecretEnvSource{LocalObjectReference: corev1.LocalObjectReference{Name: names[i]}}},
|
||||
},
|
||||
}
|
||||
containers = append(containers, container)
|
||||
}
|
||||
return containers
|
||||
}
|
||||
|
@@ -6,8 +6,10 @@ import (
|
||||
"time"
|
||||
|
||||
kubeSecrets "github.com/1Password/onepassword-operator/pkg/kubernetessecrets"
|
||||
"github.com/1Password/onepassword-operator/pkg/utils"
|
||||
|
||||
"github.com/1Password/connect-sdk-go/connect"
|
||||
"github.com/1Password/connect-sdk-go/onepassword"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
@@ -15,19 +17,22 @@ import (
|
||||
)
|
||||
|
||||
const envHostVariable = "OP_HOST"
|
||||
const lockTag = "operator.1password.io:ignore-secret"
|
||||
|
||||
var log = logf.Log.WithName("update_op_kubernetes_secrets_task")
|
||||
|
||||
func NewManager(kubernetesClient client.Client, opConnectClient connect.Client) *SecretUpdateHandler {
|
||||
func NewManager(kubernetesClient client.Client, opConnectClient connect.Client, shouldAutoRestartDeploymentsGlobal bool) *SecretUpdateHandler {
|
||||
return &SecretUpdateHandler{
|
||||
client: kubernetesClient,
|
||||
opConnectClient: opConnectClient,
|
||||
client: kubernetesClient,
|
||||
opConnectClient: opConnectClient,
|
||||
shouldAutoRestartDeploymentsGlobal: shouldAutoRestartDeploymentsGlobal,
|
||||
}
|
||||
}
|
||||
|
||||
type SecretUpdateHandler struct {
|
||||
client client.Client
|
||||
opConnectClient connect.Client
|
||||
client client.Client
|
||||
opConnectClient connect.Client
|
||||
shouldAutoRestartDeploymentsGlobal bool
|
||||
}
|
||||
|
||||
func (h *SecretUpdateHandler) UpdateKubernetesSecretsTask() error {
|
||||
@@ -39,7 +44,7 @@ func (h *SecretUpdateHandler) UpdateKubernetesSecretsTask() error {
|
||||
return h.restartDeploymentsWithUpdatedSecrets(updatedKubernetesSecrets)
|
||||
}
|
||||
|
||||
func (h *SecretUpdateHandler) restartDeploymentsWithUpdatedSecrets(updatedSecretsByNamespace map[string]map[string]bool) error {
|
||||
func (h *SecretUpdateHandler) restartDeploymentsWithUpdatedSecrets(updatedSecretsByNamespace map[string]map[string]*corev1.Secret) error {
|
||||
// No secrets to update. Exit
|
||||
if len(updatedSecretsByNamespace) == 0 || updatedSecretsByNamespace == nil {
|
||||
return nil
|
||||
@@ -52,22 +57,38 @@ func (h *SecretUpdateHandler) restartDeploymentsWithUpdatedSecrets(updatedSecret
|
||||
return err
|
||||
}
|
||||
|
||||
if len(deployments.Items) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
setForAutoRestartByNamespaceMap, err := h.getIsSetForAutoRestartByNamespaceMap()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i := 0; i < len(deployments.Items); i++ {
|
||||
deployment := &deployments.Items[i]
|
||||
updatedSecrets := updatedSecretsByNamespace[deployment.Namespace]
|
||||
secretName := deployment.Annotations[NameAnnotation]
|
||||
log.Info(fmt.Sprintf("Looking at secret %v for deployment %v", secretName, deployment.Name))
|
||||
if isUpdatedSecret(secretName, updatedSecrets) || IsDeploymentUsingSecrets(deployment, updatedSecrets) {
|
||||
h.restartDeployment(deployment)
|
||||
} else {
|
||||
log.Info(fmt.Sprintf("Deployment '%v' is up to date", deployment.GetName()))
|
||||
|
||||
updatedDeploymentSecrets := GetUpdatedSecretsForDeployment(deployment, updatedSecrets)
|
||||
if len(updatedDeploymentSecrets) == 0 {
|
||||
continue
|
||||
}
|
||||
for _, secret := range updatedDeploymentSecrets {
|
||||
if isSecretSetForAutoRestart(secret, deployment, setForAutoRestartByNamespaceMap) {
|
||||
h.restartDeployment(deployment)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
log.Info(fmt.Sprintf("Deployment %q at namespace %q is up to date", deployment.GetName(), deployment.Namespace))
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *SecretUpdateHandler) restartDeployment(deployment *appsv1.Deployment) {
|
||||
log.Info(fmt.Sprintf("Deployment '%v' references an updated secret. Restarting", deployment.GetName()))
|
||||
log.Info(fmt.Sprintf("Deployment %q at namespace %q references an updated secret. Restarting", deployment.GetName(), deployment.Namespace))
|
||||
deployment.Spec.Template.Annotations = map[string]string{
|
||||
RestartAnnotation: time.Now().String(),
|
||||
}
|
||||
@@ -77,7 +98,7 @@ func (h *SecretUpdateHandler) restartDeployment(deployment *appsv1.Deployment) {
|
||||
}
|
||||
}
|
||||
|
||||
func (h *SecretUpdateHandler) updateKubernetesSecrets() (map[string]map[string]bool, error) {
|
||||
func (h *SecretUpdateHandler) updateKubernetesSecrets() (map[string]map[string]*corev1.Secret, error) {
|
||||
secrets := &corev1.SecretList{}
|
||||
err := h.client.List(context.Background(), secrets)
|
||||
if err != nil {
|
||||
@@ -85,7 +106,7 @@ func (h *SecretUpdateHandler) updateKubernetesSecrets() (map[string]map[string]b
|
||||
return nil, err
|
||||
}
|
||||
|
||||
updatedSecrets := map[string]map[string]bool{}
|
||||
updatedSecrets := map[string]map[string]*corev1.Secret{}
|
||||
for i := 0; i < len(secrets.Items); i++ {
|
||||
secret := secrets.Items[i]
|
||||
|
||||
@@ -97,28 +118,106 @@ func (h *SecretUpdateHandler) updateKubernetesSecrets() (map[string]map[string]b
|
||||
|
||||
item, err := GetOnePasswordItemByPath(h.opConnectClient, secret.Annotations[ItemPathAnnotation])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to retrieve item: %v", err)
|
||||
log.Error(err, "failed to retrieve 1Password item at path \"%s\" for secret \"%s\"", secret.Annotations[ItemPathAnnotation], secret.Name)
|
||||
continue
|
||||
}
|
||||
|
||||
itemVersion := fmt.Sprint(item.Version)
|
||||
if currentVersion != itemVersion {
|
||||
if isItemLockedForForcedRestarts(item) {
|
||||
log.Info(fmt.Sprintf("Secret '%v' has been updated in 1Password but is set to be ignored. Updates to an ignored secret will not trigger an update to a kubernetes secret or a rolling restart.", secret.GetName()))
|
||||
secret.Annotations[VersionAnnotation] = itemVersion
|
||||
h.client.Update(context.Background(), &secret)
|
||||
continue
|
||||
}
|
||||
log.Info(fmt.Sprintf("Updating kubernetes secret '%v'", secret.GetName()))
|
||||
secret.Annotations[VersionAnnotation] = itemVersion
|
||||
updatedSecret := kubeSecrets.BuildKubernetesSecretFromOnePasswordItem(secret.Name, secret.Namespace, secret.Annotations, *item)
|
||||
updatedSecret := kubeSecrets.BuildKubernetesSecretFromOnePasswordItem(secret.Name, secret.Namespace, secret.Annotations, secret.Labels, string(secret.Type), *item)
|
||||
h.client.Update(context.Background(), updatedSecret)
|
||||
if updatedSecrets[secret.Namespace] == nil {
|
||||
updatedSecrets[secret.Namespace] = make(map[string]bool)
|
||||
updatedSecrets[secret.Namespace] = make(map[string]*corev1.Secret)
|
||||
}
|
||||
updatedSecrets[secret.Namespace][secret.Name] = true
|
||||
updatedSecrets[secret.Namespace][secret.Name] = &secret
|
||||
}
|
||||
}
|
||||
return updatedSecrets, nil
|
||||
}
|
||||
|
||||
func isUpdatedSecret(secretName string, updatedSecrets map[string]bool) bool {
|
||||
func isItemLockedForForcedRestarts(item *onepassword.Item) bool {
|
||||
tags := item.Tags
|
||||
for i := 0; i < len(tags); i++ {
|
||||
if tags[i] == lockTag {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isUpdatedSecret(secretName string, updatedSecrets map[string]*corev1.Secret) bool {
|
||||
_, ok := updatedSecrets[secretName]
|
||||
if ok {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (h *SecretUpdateHandler) getIsSetForAutoRestartByNamespaceMap() (map[string]bool, error) {
|
||||
namespaces := &corev1.NamespaceList{}
|
||||
err := h.client.List(context.Background(), namespaces)
|
||||
if err != nil {
|
||||
log.Error(err, "Failed to list kubernetes namespaces")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
namespacesMap := map[string]bool{}
|
||||
|
||||
for _, namespace := range namespaces.Items {
|
||||
namespacesMap[namespace.Name] = h.isNamespaceSetToAutoRestart(&namespace)
|
||||
}
|
||||
return namespacesMap, nil
|
||||
}
|
||||
|
||||
func isSecretSetForAutoRestart(secret *corev1.Secret, deployment *appsv1.Deployment, setForAutoRestartByNamespace map[string]bool) bool {
|
||||
restartDeployment := secret.Annotations[RestartDeploymentsAnnotation]
|
||||
//If annotation for auto restarts for deployment is not set. Check for the annotation on its namepsace
|
||||
if restartDeployment == "" {
|
||||
return isDeploymentSetForAutoRestart(deployment, setForAutoRestartByNamespace)
|
||||
}
|
||||
|
||||
restartDeploymentBool, err := utils.StringToBool(restartDeployment)
|
||||
if err != nil {
|
||||
log.Error(err, "Error parsing %v annotation on Secret %v. Must be true or false. Defaulting to false.", RestartDeploymentsAnnotation, secret.Name)
|
||||
return false
|
||||
}
|
||||
return restartDeploymentBool
|
||||
}
|
||||
|
||||
func isDeploymentSetForAutoRestart(deployment *appsv1.Deployment, setForAutoRestartByNamespace map[string]bool) bool {
|
||||
restartDeployment := deployment.Annotations[RestartDeploymentsAnnotation]
|
||||
//If annotation for auto restarts for deployment is not set. Check for the annotation on its namepsace
|
||||
if restartDeployment == "" {
|
||||
return setForAutoRestartByNamespace[deployment.Namespace]
|
||||
}
|
||||
|
||||
restartDeploymentBool, err := utils.StringToBool(restartDeployment)
|
||||
if err != nil {
|
||||
log.Error(err, "Error parsing %v annotation on Deployment %v. Must be true or false. Defaulting to false.", RestartDeploymentsAnnotation, deployment.Name)
|
||||
return false
|
||||
}
|
||||
return restartDeploymentBool
|
||||
}
|
||||
|
||||
func (h *SecretUpdateHandler) isNamespaceSetToAutoRestart(namespace *corev1.Namespace) bool {
|
||||
restartDeployment := namespace.Annotations[RestartDeploymentsAnnotation]
|
||||
//If annotation for auto restarts for deployment is not set. Check environment variable set on the operator
|
||||
if restartDeployment == "" {
|
||||
return h.shouldAutoRestartDeploymentsGlobal
|
||||
}
|
||||
|
||||
restartDeploymentBool, err := utils.StringToBool(restartDeployment)
|
||||
if err != nil {
|
||||
log.Error(err, "Error parsing %v annotation on Namespace %v. Must be true or false. Defaulting to false.", RestartDeploymentsAnnotation, namespace.Name)
|
||||
return false
|
||||
}
|
||||
return restartDeploymentBool
|
||||
}
|
||||
|
@@ -34,14 +34,16 @@ const (
|
||||
)
|
||||
|
||||
type testUpdateSecretTask struct {
|
||||
testName string
|
||||
existingDeployment *appsv1.Deployment
|
||||
existingSecret *corev1.Secret
|
||||
expectedError error
|
||||
expectedResultSecret *corev1.Secret
|
||||
expectedEvents []string
|
||||
opItem map[string]string
|
||||
expectedRestart bool
|
||||
testName string
|
||||
existingDeployment *appsv1.Deployment
|
||||
existingNamespace *corev1.Namespace
|
||||
existingSecret *corev1.Secret
|
||||
expectedError error
|
||||
expectedResultSecret *corev1.Secret
|
||||
expectedEvents []string
|
||||
opItem map[string]string
|
||||
expectedRestart bool
|
||||
globalAutoRestartEnabled bool
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -52,9 +54,16 @@ var (
|
||||
itemPath = fmt.Sprintf("vaults/%v/items/%v", vaultId, itemId)
|
||||
)
|
||||
|
||||
var defaultNamespace = &corev1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: namespace,
|
||||
},
|
||||
}
|
||||
|
||||
var tests = []testUpdateSecretTask{
|
||||
{
|
||||
testName: "Test unrelated deployment is not restarted with an updated secret",
|
||||
testName: "Test unrelated deployment is not restarted with an updated secret",
|
||||
existingNamespace: defaultNamespace,
|
||||
existingDeployment: &appsv1.Deployment{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: deploymentKind,
|
||||
@@ -96,10 +105,12 @@ var tests = []testUpdateSecretTask{
|
||||
userKey: username,
|
||||
passKey: password,
|
||||
},
|
||||
expectedRestart: false,
|
||||
expectedRestart: false,
|
||||
globalAutoRestartEnabled: true,
|
||||
},
|
||||
{
|
||||
testName: "OP item has new version. Secret needs update. Deployment is restarted based on containers",
|
||||
testName: "OP item has new version. Secret needs update. Deployment is restarted based on containers",
|
||||
existingNamespace: defaultNamespace,
|
||||
existingDeployment: &appsv1.Deployment{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: deploymentKind,
|
||||
@@ -160,10 +171,12 @@ var tests = []testUpdateSecretTask{
|
||||
userKey: username,
|
||||
passKey: password,
|
||||
},
|
||||
expectedRestart: true,
|
||||
expectedRestart: true,
|
||||
globalAutoRestartEnabled: true,
|
||||
},
|
||||
{
|
||||
testName: "OP item has new version. Secret needs update. Deployment is restarted based on annotation",
|
||||
testName: "OP item has new version. Secret needs update. Deployment is restarted based on annotation",
|
||||
existingNamespace: defaultNamespace,
|
||||
existingDeployment: &appsv1.Deployment{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: deploymentKind,
|
||||
@@ -205,10 +218,12 @@ var tests = []testUpdateSecretTask{
|
||||
userKey: username,
|
||||
passKey: password,
|
||||
},
|
||||
expectedRestart: true,
|
||||
expectedRestart: true,
|
||||
globalAutoRestartEnabled: true,
|
||||
},
|
||||
{
|
||||
testName: "OP item has new version. Secret needs update. Deployment is restarted based on volume",
|
||||
testName: "OP item has new version. Secret needs update. Deployment is restarted based on volume",
|
||||
existingNamespace: defaultNamespace,
|
||||
existingDeployment: &appsv1.Deployment{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: deploymentKind,
|
||||
@@ -262,10 +277,12 @@ var tests = []testUpdateSecretTask{
|
||||
userKey: username,
|
||||
passKey: password,
|
||||
},
|
||||
expectedRestart: true,
|
||||
expectedRestart: true,
|
||||
globalAutoRestartEnabled: true,
|
||||
},
|
||||
{
|
||||
testName: "No secrets need update. No deployment is restarted",
|
||||
testName: "No secrets need update. No deployment is restarted",
|
||||
existingNamespace: defaultNamespace,
|
||||
existingDeployment: &appsv1.Deployment{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: deploymentKind,
|
||||
@@ -307,11 +324,440 @@ var tests = []testUpdateSecretTask{
|
||||
userKey: username,
|
||||
passKey: password,
|
||||
},
|
||||
expectedRestart: false,
|
||||
expectedRestart: false,
|
||||
globalAutoRestartEnabled: true,
|
||||
},
|
||||
{
|
||||
testName: `Deployment is not restarted when no auto restart is set to true for all
|
||||
deployments and is not overwritten by by a namespace or deployment annotation`,
|
||||
existingNamespace: defaultNamespace,
|
||||
existingDeployment: &appsv1.Deployment{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: deploymentKind,
|
||||
APIVersion: deploymentAPIVersion,
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: appsv1.DeploymentSpec{
|
||||
Template: corev1.PodTemplateSpec{
|
||||
Spec: corev1.PodSpec{
|
||||
Containers: []corev1.Container{
|
||||
{
|
||||
Env: []corev1.EnvVar{
|
||||
{
|
||||
Name: name,
|
||||
ValueFrom: &corev1.EnvVarSource{
|
||||
SecretKeyRef: &corev1.SecretKeySelector{
|
||||
LocalObjectReference: corev1.LocalObjectReference{
|
||||
Name: name,
|
||||
},
|
||||
Key: passKey,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
existingSecret: &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
Annotations: map[string]string{
|
||||
VersionAnnotation: "old version",
|
||||
ItemPathAnnotation: itemPath,
|
||||
},
|
||||
},
|
||||
Data: expectedSecretData,
|
||||
},
|
||||
expectedError: nil,
|
||||
expectedResultSecret: &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
Annotations: map[string]string{
|
||||
VersionAnnotation: fmt.Sprint(itemVersion),
|
||||
ItemPathAnnotation: itemPath,
|
||||
},
|
||||
},
|
||||
Data: expectedSecretData,
|
||||
},
|
||||
opItem: map[string]string{
|
||||
userKey: username,
|
||||
passKey: password,
|
||||
},
|
||||
expectedRestart: false,
|
||||
globalAutoRestartEnabled: false,
|
||||
},
|
||||
{
|
||||
testName: `Secret autostart true value takes precedence over false deployment value`,
|
||||
existingNamespace: defaultNamespace,
|
||||
existingDeployment: &appsv1.Deployment{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: deploymentKind,
|
||||
APIVersion: deploymentAPIVersion,
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
Annotations: map[string]string{
|
||||
RestartDeploymentsAnnotation: "false",
|
||||
},
|
||||
},
|
||||
Spec: appsv1.DeploymentSpec{
|
||||
Template: corev1.PodTemplateSpec{
|
||||
Spec: corev1.PodSpec{
|
||||
Containers: []corev1.Container{
|
||||
{
|
||||
Env: []corev1.EnvVar{
|
||||
{
|
||||
Name: name,
|
||||
ValueFrom: &corev1.EnvVarSource{
|
||||
SecretKeyRef: &corev1.SecretKeySelector{
|
||||
LocalObjectReference: corev1.LocalObjectReference{
|
||||
Name: name,
|
||||
},
|
||||
Key: passKey,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
existingSecret: &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
Annotations: map[string]string{
|
||||
VersionAnnotation: "old version",
|
||||
ItemPathAnnotation: itemPath,
|
||||
RestartDeploymentsAnnotation: "true",
|
||||
},
|
||||
},
|
||||
Data: expectedSecretData,
|
||||
},
|
||||
expectedError: nil,
|
||||
expectedResultSecret: &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
Annotations: map[string]string{
|
||||
VersionAnnotation: fmt.Sprint(itemVersion),
|
||||
ItemPathAnnotation: itemPath,
|
||||
RestartDeploymentsAnnotation: "true",
|
||||
},
|
||||
},
|
||||
Data: expectedSecretData,
|
||||
},
|
||||
opItem: map[string]string{
|
||||
userKey: username,
|
||||
passKey: password,
|
||||
},
|
||||
expectedRestart: true,
|
||||
globalAutoRestartEnabled: false,
|
||||
},
|
||||
{
|
||||
testName: `Secret autostart true value takes precedence over false deployment value`,
|
||||
existingNamespace: defaultNamespace,
|
||||
existingDeployment: &appsv1.Deployment{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: deploymentKind,
|
||||
APIVersion: deploymentAPIVersion,
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
Annotations: map[string]string{
|
||||
RestartDeploymentsAnnotation: "true",
|
||||
},
|
||||
},
|
||||
Spec: appsv1.DeploymentSpec{
|
||||
Template: corev1.PodTemplateSpec{
|
||||
Spec: corev1.PodSpec{
|
||||
Containers: []corev1.Container{
|
||||
{
|
||||
Env: []corev1.EnvVar{
|
||||
{
|
||||
Name: name,
|
||||
ValueFrom: &corev1.EnvVarSource{
|
||||
SecretKeyRef: &corev1.SecretKeySelector{
|
||||
LocalObjectReference: corev1.LocalObjectReference{
|
||||
Name: name,
|
||||
},
|
||||
Key: passKey,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
existingSecret: &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
Annotations: map[string]string{
|
||||
VersionAnnotation: "old version",
|
||||
ItemPathAnnotation: itemPath,
|
||||
RestartDeploymentsAnnotation: "false",
|
||||
},
|
||||
},
|
||||
Data: expectedSecretData,
|
||||
},
|
||||
expectedError: nil,
|
||||
expectedResultSecret: &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
Annotations: map[string]string{
|
||||
VersionAnnotation: fmt.Sprint(itemVersion),
|
||||
ItemPathAnnotation: itemPath,
|
||||
RestartDeploymentsAnnotation: "false",
|
||||
},
|
||||
},
|
||||
Data: expectedSecretData,
|
||||
},
|
||||
opItem: map[string]string{
|
||||
userKey: username,
|
||||
passKey: password,
|
||||
},
|
||||
expectedRestart: false,
|
||||
globalAutoRestartEnabled: true,
|
||||
},
|
||||
{
|
||||
testName: `Deployment autostart true value takes precedence over false global auto restart value`,
|
||||
existingNamespace: defaultNamespace,
|
||||
existingDeployment: &appsv1.Deployment{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: deploymentKind,
|
||||
APIVersion: deploymentAPIVersion,
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
Annotations: map[string]string{
|
||||
RestartDeploymentsAnnotation: "true",
|
||||
},
|
||||
},
|
||||
Spec: appsv1.DeploymentSpec{
|
||||
Template: corev1.PodTemplateSpec{
|
||||
Spec: corev1.PodSpec{
|
||||
Containers: []corev1.Container{
|
||||
{
|
||||
Env: []corev1.EnvVar{
|
||||
{
|
||||
Name: name,
|
||||
ValueFrom: &corev1.EnvVarSource{
|
||||
SecretKeyRef: &corev1.SecretKeySelector{
|
||||
LocalObjectReference: corev1.LocalObjectReference{
|
||||
Name: name,
|
||||
},
|
||||
Key: passKey,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
existingSecret: &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
Annotations: map[string]string{
|
||||
VersionAnnotation: "old version",
|
||||
ItemPathAnnotation: itemPath,
|
||||
},
|
||||
},
|
||||
Data: expectedSecretData,
|
||||
},
|
||||
expectedError: nil,
|
||||
expectedResultSecret: &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
Annotations: map[string]string{
|
||||
VersionAnnotation: fmt.Sprint(itemVersion),
|
||||
ItemPathAnnotation: itemPath,
|
||||
},
|
||||
},
|
||||
Data: expectedSecretData,
|
||||
},
|
||||
opItem: map[string]string{
|
||||
userKey: username,
|
||||
passKey: password,
|
||||
},
|
||||
expectedRestart: true,
|
||||
globalAutoRestartEnabled: false,
|
||||
},
|
||||
{
|
||||
testName: `Deployment autostart false value takes precedence over false global auto restart value,
|
||||
and true namespace value.`,
|
||||
existingNamespace: &corev1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: namespace,
|
||||
Annotations: map[string]string{
|
||||
RestartDeploymentsAnnotation: "true",
|
||||
},
|
||||
},
|
||||
},
|
||||
existingDeployment: &appsv1.Deployment{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: deploymentKind,
|
||||
APIVersion: deploymentAPIVersion,
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
Annotations: map[string]string{
|
||||
RestartDeploymentsAnnotation: "false",
|
||||
},
|
||||
},
|
||||
Spec: appsv1.DeploymentSpec{
|
||||
Template: corev1.PodTemplateSpec{
|
||||
Spec: corev1.PodSpec{
|
||||
Containers: []corev1.Container{
|
||||
{
|
||||
Env: []corev1.EnvVar{
|
||||
{
|
||||
Name: name,
|
||||
ValueFrom: &corev1.EnvVarSource{
|
||||
SecretKeyRef: &corev1.SecretKeySelector{
|
||||
LocalObjectReference: corev1.LocalObjectReference{
|
||||
Name: name,
|
||||
},
|
||||
Key: passKey,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
existingSecret: &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
Annotations: map[string]string{
|
||||
VersionAnnotation: "old version",
|
||||
ItemPathAnnotation: itemPath,
|
||||
},
|
||||
},
|
||||
Data: expectedSecretData,
|
||||
},
|
||||
expectedError: nil,
|
||||
expectedResultSecret: &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
Annotations: map[string]string{
|
||||
VersionAnnotation: fmt.Sprint(itemVersion),
|
||||
ItemPathAnnotation: itemPath,
|
||||
},
|
||||
},
|
||||
Data: expectedSecretData,
|
||||
},
|
||||
opItem: map[string]string{
|
||||
userKey: username,
|
||||
passKey: password,
|
||||
},
|
||||
expectedRestart: false,
|
||||
globalAutoRestartEnabled: false,
|
||||
},
|
||||
{
|
||||
testName: `Namespace autostart true value takes precedence over false global auto restart value`,
|
||||
existingNamespace: &corev1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: namespace,
|
||||
Annotations: map[string]string{
|
||||
RestartDeploymentsAnnotation: "true",
|
||||
},
|
||||
},
|
||||
},
|
||||
existingDeployment: &appsv1.Deployment{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: deploymentKind,
|
||||
APIVersion: deploymentAPIVersion,
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: appsv1.DeploymentSpec{
|
||||
Template: corev1.PodTemplateSpec{
|
||||
Spec: corev1.PodSpec{
|
||||
Containers: []corev1.Container{
|
||||
{
|
||||
Env: []corev1.EnvVar{
|
||||
{
|
||||
Name: name,
|
||||
ValueFrom: &corev1.EnvVarSource{
|
||||
SecretKeyRef: &corev1.SecretKeySelector{
|
||||
LocalObjectReference: corev1.LocalObjectReference{
|
||||
Name: name,
|
||||
},
|
||||
Key: passKey,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
existingSecret: &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
Annotations: map[string]string{
|
||||
VersionAnnotation: "old version",
|
||||
ItemPathAnnotation: itemPath,
|
||||
},
|
||||
},
|
||||
Data: expectedSecretData,
|
||||
},
|
||||
expectedError: nil,
|
||||
expectedResultSecret: &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
Annotations: map[string]string{
|
||||
VersionAnnotation: fmt.Sprint(itemVersion),
|
||||
ItemPathAnnotation: itemPath,
|
||||
},
|
||||
},
|
||||
Data: expectedSecretData,
|
||||
},
|
||||
opItem: map[string]string{
|
||||
userKey: username,
|
||||
passKey: password,
|
||||
},
|
||||
expectedRestart: true,
|
||||
globalAutoRestartEnabled: false,
|
||||
},
|
||||
}
|
||||
|
||||
func TestReconcileDepoyment(t *testing.T) {
|
||||
func TestUpdateSecretHandler(t *testing.T) {
|
||||
for _, testData := range tests {
|
||||
t.Run(testData.testName, func(t *testing.T) {
|
||||
|
||||
@@ -322,6 +768,7 @@ func TestReconcileDepoyment(t *testing.T) {
|
||||
// Objects to track in the fake client.
|
||||
objs := []runtime.Object{
|
||||
testData.existingDeployment,
|
||||
testData.existingNamespace,
|
||||
}
|
||||
|
||||
if testData.existingSecret != nil {
|
||||
@@ -342,8 +789,9 @@ func TestReconcileDepoyment(t *testing.T) {
|
||||
return &item, nil
|
||||
}
|
||||
h := &SecretUpdateHandler{
|
||||
client: cl,
|
||||
opConnectClient: opConnectClient,
|
||||
client: cl,
|
||||
opConnectClient: opConnectClient,
|
||||
shouldAutoRestartDeploymentsGlobal: testData.globalAutoRestartEnabled,
|
||||
}
|
||||
|
||||
err := h.UpdateKubernetesSecretsTask()
|
||||
@@ -377,9 +825,9 @@ func TestReconcileDepoyment(t *testing.T) {
|
||||
|
||||
_, ok := deployment.Spec.Template.Annotations[RestartAnnotation]
|
||||
if ok {
|
||||
assert.True(t, testData.expectedRestart)
|
||||
assert.True(t, testData.expectedRestart, "Expected deployment to restart but it did not")
|
||||
} else {
|
||||
assert.False(t, testData.expectedRestart)
|
||||
assert.False(t, testData.expectedRestart, "Deployment was restarted but should not have been.")
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -388,12 +836,12 @@ func TestReconcileDepoyment(t *testing.T) {
|
||||
func TestIsUpdatedSecret(t *testing.T) {
|
||||
|
||||
secretName := "test-secret"
|
||||
updatedSecrets := map[string]bool{
|
||||
"some_secret": true,
|
||||
updatedSecrets := map[string]*corev1.Secret{
|
||||
"some_secret": &corev1.Secret{},
|
||||
}
|
||||
assert.False(t, isUpdatedSecret(secretName, updatedSecrets))
|
||||
|
||||
updatedSecrets[secretName] = true
|
||||
updatedSecrets[secretName] = &corev1.Secret{}
|
||||
assert.True(t, isUpdatedSecret(secretName, updatedSecrets))
|
||||
}
|
||||
|
||||
|
@@ -2,7 +2,7 @@ package onepassword
|
||||
|
||||
import corev1 "k8s.io/api/core/v1"
|
||||
|
||||
func AreVolumesUsingSecrets(volumes []corev1.Volume, secrets map[string]bool) bool {
|
||||
func AreVolumesUsingSecrets(volumes []corev1.Volume, secrets map[string]*corev1.Secret) bool {
|
||||
for i := 0; i < len(volumes); i++ {
|
||||
if secret := volumes[i].Secret; secret != nil {
|
||||
secretName := secret.SecretName
|
||||
@@ -14,3 +14,16 @@ func AreVolumesUsingSecrets(volumes []corev1.Volume, secrets map[string]bool) bo
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func AppendUpdatedVolumeSecrets(volumes []corev1.Volume, secrets map[string]*corev1.Secret, updatedDeploymentSecrets map[string]*corev1.Secret) map[string]*corev1.Secret {
|
||||
for i := 0; i < len(volumes); i++ {
|
||||
if secret := volumes[i].Secret; secret != nil {
|
||||
secretName := secret.SecretName
|
||||
secret, ok := secrets[secretName]
|
||||
if ok {
|
||||
updatedDeploymentSecrets[secret.Name] = secret
|
||||
}
|
||||
}
|
||||
}
|
||||
return updatedDeploymentSecrets
|
||||
}
|
||||
|
@@ -2,12 +2,14 @@ package onepassword
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
func TestAreVolmesUsingSecrets(t *testing.T) {
|
||||
secretNamesToSearch := map[string]bool{
|
||||
"onepassword-database-secret": true,
|
||||
"onepassword-api-key": true,
|
||||
secretNamesToSearch := map[string]*corev1.Secret{
|
||||
"onepassword-database-secret": &corev1.Secret{},
|
||||
"onepassword-api-key": &corev1.Secret{},
|
||||
}
|
||||
|
||||
volumeSecretNames := []string{
|
||||
@@ -24,9 +26,9 @@ func TestAreVolmesUsingSecrets(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAreVolumesNotUsingSecrets(t *testing.T) {
|
||||
secretNamesToSearch := map[string]bool{
|
||||
"onepassword-database-secret": true,
|
||||
"onepassword-api-key": true,
|
||||
secretNamesToSearch := map[string]*corev1.Secret{
|
||||
"onepassword-database-secret": &corev1.Secret{},
|
||||
"onepassword-api-key": &corev1.Secret{},
|
||||
}
|
||||
|
||||
volumeSecretNames := []string{
|
||||
|
@@ -1,5 +1,10 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func ContainsString(slice []string, s string) bool {
|
||||
for _, item := range slice {
|
||||
if item == s {
|
||||
@@ -18,3 +23,11 @@ func RemoveString(slice []string, s string) (result []string) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func StringToBool(str string) (bool, error) {
|
||||
restartDeploymentBool, err := strconv.ParseBool(strings.ToLower(str))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return restartDeploymentBool, nil
|
||||
}
|
||||
|
104
scripts/prepare-release.sh
Executable file
104
scripts/prepare-release.sh
Executable file
@@ -0,0 +1,104 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# prepare-release.sh
|
||||
# (Note: This should be called by `make release/prepare` because it depends
|
||||
# on several variables set by the Makefile)
|
||||
#
|
||||
# Performs release preparation tasks:
|
||||
# - Creates a release branch
|
||||
# - Renames "LATEST" section to the new version number
|
||||
# - Adds new "LATEST" entry to the changelog
|
||||
#
|
||||
##############################################
|
||||
set -Eeuo pipefail
|
||||
|
||||
if [[ -z "${NEW_VERSION:-}" ]]; then
|
||||
echo "[ERROR] NEW_VERSION environment variable not defined." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Script called from within a git repo?
|
||||
if [[ $(git rev-parse --is-inside-work-tree &>/dev/null) -ne 0 ]]; then
|
||||
echo "[ERROR] Current directory (${SRCDIR}) is not a git repository" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
REPO_ROOT=$(git rev-parse --show-toplevel)
|
||||
CHANGELOG_FILENAME=${CHANGELOG:-"CHANGELOG.md"}
|
||||
|
||||
# normalize version by removing `v` prefix
|
||||
VERSION_NUM=${NEW_VERSION/#v/}
|
||||
RELEASE_BRANCH=$(printf "release/v%s" "${VERSION_NUM}")
|
||||
|
||||
function updateChangelog() {
|
||||
local tmpfile
|
||||
|
||||
trap '[ -e "${tmpfile}" ] && rm "${tmpfile}"' RETURN
|
||||
|
||||
local changelogFile
|
||||
changelogFile=$(printf "%s/%s" "${REPO_ROOT}" "${CHANGELOG_FILENAME}")
|
||||
|
||||
# create Changelog file if not exists
|
||||
if ! [[ -f "${REPO_ROOT}/${CHANGELOG_FILENAME}" ]]; then
|
||||
touch "${REPO_ROOT}/${CHANGELOG_FILENAME}" && \
|
||||
git add "${REPO_ROOT}/${CHANGELOG_FILENAME}"
|
||||
fi
|
||||
|
||||
tmpfile=$(mktemp)
|
||||
|
||||
# Replace "Latest" in the top-most changelog block with new version
|
||||
# Then push a new "latest" block to top of the changelog
|
||||
awk 'NR==1, /---/{ sub(/START\/LATEST/, "START/v'${VERSION_NUM}'"); sub(/# Latest/, "# v'${VERSION_NUM}'") } {print}' \
|
||||
"${changelogFile}" > "${tmpfile}"
|
||||
|
||||
# Inserts "Latest" changelog HEREDOC at the top of the file
|
||||
cat - "${tmpfile}" << EOF > "${REPO_ROOT}/${CHANGELOG_FILENAME}"
|
||||
[//]: # (START/LATEST)
|
||||
# Latest
|
||||
|
||||
## Features
|
||||
* A user-friendly description of a new feature. {issue-number}
|
||||
|
||||
## Fixes
|
||||
* A user-friendly description of a fix. {issue-number}
|
||||
|
||||
## Security
|
||||
* A user-friendly description of a security fix. {issue-number}
|
||||
|
||||
---
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
function _main() {
|
||||
|
||||
# Stash version changes
|
||||
git stash push &>/dev/null
|
||||
|
||||
if ! git checkout -b "${RELEASE_BRANCH}" origin/"${MAIN_BRANCH:-main}"; then
|
||||
echo "[ERROR] Could not check out release branch." >&2
|
||||
git stash pop &>/dev/null
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Add the version changes to release branch
|
||||
git stash pop &>/dev/null
|
||||
|
||||
updateChangelog
|
||||
|
||||
cat << EOF
|
||||
|
||||
[SUCCESS] Changelog updated & release branch created:
|
||||
New Version: ${NEW_VERSION}
|
||||
Release Branch: ${RELEASE_BRANCH}
|
||||
|
||||
Next steps:
|
||||
1. Edit the changelog notes in ${CHANGELOG_FILENAME}
|
||||
2. Commit changes to the release branch
|
||||
3. Push changes to remote => git push origin ${RELEASE_BRANCH}
|
||||
|
||||
EOF
|
||||
exit 0
|
||||
}
|
||||
|
||||
_main
|
21
vendor/github.com/1Password/connect-sdk-go/LICENSE
generated
vendored
Normal file
21
vendor/github.com/1Password/connect-sdk-go/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 1Password
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
223
vendor/github.com/1Password/connect-sdk-go/connect/client.go
generated
vendored
223
vendor/github.com/1Password/connect-sdk-go/connect/client.go
generated
vendored
@@ -3,6 +3,7 @@ package connect
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
@@ -10,20 +11,22 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
|
||||
"github.com/1Password/connect-sdk-go/onepassword"
|
||||
opentracing "github.com/opentracing/opentracing-go"
|
||||
"github.com/opentracing/opentracing-go/ext"
|
||||
jaegerClientConfig "github.com/uber/jaeger-client-go/config"
|
||||
"github.com/uber/jaeger-client-go/zipkin"
|
||||
|
||||
"github.com/1Password/connect-sdk-go/onepassword"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultUserAgent = "connect-sdk-go/0.0.1"
|
||||
defaultUserAgent = "connect-sdk-go/%s"
|
||||
)
|
||||
|
||||
// Client Represents an available 1Password Connect API to connect to
|
||||
type Client interface {
|
||||
GetVaults() ([]onepassword.Vault, error)
|
||||
GetVault(uuid string) (*onepassword.Vault, error)
|
||||
GetVaultsByTitle(uuid string) ([]onepassword.Vault, error)
|
||||
GetItem(uuid string, vaultUUID string) (*onepassword.Item, error)
|
||||
GetItems(vaultUUID string) ([]onepassword.Item, error)
|
||||
@@ -32,6 +35,8 @@ type Client interface {
|
||||
CreateItem(item *onepassword.Item, vaultUUID string) (*onepassword.Item, error)
|
||||
UpdateItem(item *onepassword.Item, vaultUUID string) (*onepassword.Item, error)
|
||||
DeleteItem(item *onepassword.Item, vaultUUID string) error
|
||||
GetFile(fileUUID string, itemUUID string, vaultUUID string) (*onepassword.File, error)
|
||||
GetFileContent(file *onepassword.File) ([]byte, error)
|
||||
}
|
||||
|
||||
type httpClient interface {
|
||||
@@ -61,7 +66,7 @@ func NewClientFromEnvironment() (Client, error) {
|
||||
|
||||
// NewClient Returns a Secret Service client for a given url and jwt
|
||||
func NewClient(url string, token string) Client {
|
||||
return NewClientWithUserAgent(url, token, defaultUserAgent)
|
||||
return NewClientWithUserAgent(url, token, fmt.Sprintf(defaultUserAgent, SDKVersion))
|
||||
}
|
||||
|
||||
// NewClientWithUserAgent Returns a Secret Service client for a given url and jwt and identifies with userAgent
|
||||
@@ -112,23 +117,41 @@ func (rs *restClient) GetVaults() ([]onepassword.Vault, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if response.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("Unable to retrieve vaults. Receieved %q for %q", response.Status, vaultURL)
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
vaults := []onepassword.Vault{}
|
||||
if err := json.Unmarshal(body, &vaults); err != nil {
|
||||
var vaults []onepassword.Vault
|
||||
if err := parseResponse(response, http.StatusOK, &vaults); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return vaults, nil
|
||||
}
|
||||
|
||||
// GetVaults Get a list of all available vaults
|
||||
func (rs *restClient) GetVault(uuid string) (*onepassword.Vault, error) {
|
||||
if uuid == "" {
|
||||
return nil, errors.New("no uuid provided")
|
||||
}
|
||||
|
||||
span := rs.tracer.StartSpan("GetVault")
|
||||
defer span.Finish()
|
||||
|
||||
vaultURL := fmt.Sprintf("/v1/vaults/%s", uuid)
|
||||
request, err := rs.buildRequest(http.MethodGet, vaultURL, http.NoBody, span)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
response, err := rs.client.Do(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var vault onepassword.Vault
|
||||
if err := parseResponse(response, http.StatusOK, &vault); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &vault, nil
|
||||
}
|
||||
|
||||
func (rs *restClient) GetVaultsByTitle(title string) ([]onepassword.Vault, error) {
|
||||
span := rs.tracer.StartSpan("GetVaultsByTitle")
|
||||
defer span.Finish()
|
||||
@@ -145,17 +168,8 @@ func (rs *restClient) GetVaultsByTitle(title string) ([]onepassword.Vault, error
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if response.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("Unable to retrieve vaults. Receieved %q for %q", response.Status, itemURL)
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
vaults := []onepassword.Vault{}
|
||||
if err := json.Unmarshal(body, &vaults); err != nil {
|
||||
var vaults []onepassword.Vault
|
||||
if err := parseResponse(response, http.StatusOK, &vaults); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -177,18 +191,8 @@ func (rs *restClient) GetItem(uuid string, vaultUUID string) (*onepassword.Item,
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if response.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("Unable to retrieve item. Receieved %q for %q", response.Status, itemURL)
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
item := onepassword.Item{}
|
||||
if err := json.Unmarshal(body, &item); err != nil {
|
||||
var item onepassword.Item
|
||||
if err := parseResponse(response, http.StatusOK, &item); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -226,17 +230,8 @@ func (rs *restClient) GetItemsByTitle(title string, vaultUUID string) ([]onepass
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if response.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("Unable to retrieve item. Receieved %q for %q", response.Status, itemURL)
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
items := []onepassword.Item{}
|
||||
if err := json.Unmarshal(body, &items); err != nil {
|
||||
var items []onepassword.Item
|
||||
if err := parseResponse(response, http.StatusOK, &items); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -258,17 +253,8 @@ func (rs *restClient) GetItems(vaultUUID string) ([]onepassword.Item, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if response.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("Unable to retrieve items. Receieved %q for %q", response.Status, itemURL)
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
items := []onepassword.Item{}
|
||||
if err := json.Unmarshal(body, &items); err != nil {
|
||||
var items []onepassword.Item
|
||||
if err := parseResponse(response, http.StatusOK, &items); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -296,17 +282,8 @@ func (rs *restClient) CreateItem(item *onepassword.Item, vaultUUID string) (*one
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if response.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("Unable to create item. Receieved %q for %q", response.Status, itemURL)
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
newItem := onepassword.Item{}
|
||||
if err := json.Unmarshal(body, &newItem); err != nil {
|
||||
var newItem onepassword.Item
|
||||
if err := parseResponse(response, http.StatusOK, &newItem); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -334,17 +311,8 @@ func (rs *restClient) UpdateItem(item *onepassword.Item, vaultUUID string) (*one
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if response.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("Unable to update item. Receieved %q for %q", response.Status, itemURL)
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
newItem := onepassword.Item{}
|
||||
if err := json.Unmarshal(body, &newItem); err != nil {
|
||||
var newItem onepassword.Item
|
||||
if err := parseResponse(response, http.StatusOK, &newItem); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -367,13 +335,73 @@ func (rs *restClient) DeleteItem(item *onepassword.Item, vaultUUID string) error
|
||||
return err
|
||||
}
|
||||
|
||||
if response.StatusCode != http.StatusNoContent {
|
||||
return fmt.Errorf("Unable to retrieve item. Receieved %q for %q", response.Status, itemURL)
|
||||
if err := parseResponse(response, http.StatusNoContent, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetFile Get a specific File in a specified item.
|
||||
// This does not include the file contents. Call GetFileContent() to load the file's content.
|
||||
func (rs *restClient) GetFile(uuid string, itemUUID string, vaultUUID string) (*onepassword.File, error) {
|
||||
span := rs.tracer.StartSpan("GetFile")
|
||||
defer span.Finish()
|
||||
|
||||
itemURL := fmt.Sprintf("/v1/vaults/%s/items/%s/files/%s", vaultUUID, itemUUID, uuid)
|
||||
request, err := rs.buildRequest(http.MethodGet, itemURL, http.NoBody, span)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
response, err := rs.client.Do(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := expectMinimumConnectVersion(response, version{1, 3, 0}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var file onepassword.File
|
||||
if err := parseResponse(response, http.StatusOK, &file); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &file, nil
|
||||
}
|
||||
|
||||
// GetFileContent retrieves the file's content.
|
||||
// If the file's content have previously been fetched, those contents are returned without making another request.
|
||||
func (rs *restClient) GetFileContent(file *onepassword.File) ([]byte, error) {
|
||||
if content, err := file.Content(); err == nil {
|
||||
return content, nil
|
||||
}
|
||||
|
||||
span := rs.tracer.StartSpan("GetFileContent")
|
||||
defer span.Finish()
|
||||
|
||||
request, err := rs.buildRequest(http.MethodGet, file.ContentPath, http.NoBody, span)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
response, err := rs.client.Do(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := expectMinimumConnectVersion(response, version{1, 3, 0}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
content, err := readResponseBody(response, http.StatusOK)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
file.SetContent(content)
|
||||
return content, nil
|
||||
}
|
||||
|
||||
func (rs *restClient) buildRequest(method string, path string, body io.Reader, span opentracing.Span) (*http.Request, error) {
|
||||
url := fmt.Sprintf("%s%s", rs.URL, path)
|
||||
|
||||
@@ -394,3 +422,32 @@ func (rs *restClient) buildRequest(method string, path string, body io.Reader, s
|
||||
|
||||
return request, nil
|
||||
}
|
||||
|
||||
func parseResponse(resp *http.Response, expectedStatusCode int, result interface{}) error {
|
||||
body, err := readResponseBody(resp, expectedStatusCode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if result != nil {
|
||||
if err := json.Unmarshal(body, result); err != nil {
|
||||
return fmt.Errorf("decoding response: %s", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func readResponseBody(resp *http.Response, expectedStatusCode int) ([]byte, error) {
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp.StatusCode != expectedStatusCode {
|
||||
var errResp *onepassword.Error
|
||||
if err := json.Unmarshal(body, &errResp); err != nil {
|
||||
return nil, fmt.Errorf("decoding error response: %s", err)
|
||||
}
|
||||
return nil, errResp
|
||||
}
|
||||
return body, nil
|
||||
}
|
||||
|
104
vendor/github.com/1Password/connect-sdk-go/connect/version.go
generated
vendored
Normal file
104
vendor/github.com/1Password/connect-sdk-go/connect/version.go
generated
vendored
Normal file
@@ -0,0 +1,104 @@
|
||||
package connect
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// SDKVersion is the latest Semantic Version of the library
|
||||
// Do not rename this variable without changing the regex in the Makefile
|
||||
const SDKVersion = "1.2.0"
|
||||
|
||||
const VersionHeaderKey = "1Password-Connect-Version"
|
||||
|
||||
// expectMinimumConnectVersion returns an error if the provided minimum version for Connect is lower than the version
|
||||
// reported in the response from Connect.
|
||||
func expectMinimumConnectVersion(resp *http.Response, minimumVersion version) error {
|
||||
serverVersion, err := getServerVersion(resp)
|
||||
if err != nil {
|
||||
// Return gracefully if server version cannot be determined reliably
|
||||
return nil
|
||||
}
|
||||
if !serverVersion.IsGreaterOrEqualThan(minimumVersion) {
|
||||
return fmt.Errorf("need at least version %s of Connect for this function, detected version %s. Please update your Connect server", minimumVersion, serverVersion)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getServerVersion(resp *http.Response) (serverVersion, error) {
|
||||
versionHeader := resp.Header.Get(VersionHeaderKey)
|
||||
if versionHeader == "" {
|
||||
// The last version without the version header was v1.2.0
|
||||
return serverVersion{
|
||||
version: version{1, 2, 0},
|
||||
orEarlier: true,
|
||||
}, nil
|
||||
}
|
||||
return parseServerVersion(versionHeader)
|
||||
}
|
||||
|
||||
type version struct {
|
||||
major int
|
||||
minor int
|
||||
patch int
|
||||
}
|
||||
|
||||
// serverVersion describes the version reported by the server.
|
||||
type serverVersion struct {
|
||||
version
|
||||
// orEarlier is true if the version is derived from the lack of a version header from the server.
|
||||
orEarlier bool
|
||||
}
|
||||
|
||||
func (v version) String() string {
|
||||
return fmt.Sprintf("%d.%d.%d", v.major, v.minor, v.patch)
|
||||
}
|
||||
|
||||
func (v serverVersion) String() string {
|
||||
if v.orEarlier {
|
||||
return v.version.String() + " (or earlier)"
|
||||
}
|
||||
return v.version.String()
|
||||
}
|
||||
|
||||
// IsGreaterOrEqualThan returns true if the lefthand-side version is equal to or or a higher version than the provided
|
||||
// minimum according to the semantic versioning rules.
|
||||
func (v version) IsGreaterOrEqualThan(min version) bool {
|
||||
if v.major != min.major {
|
||||
// Different major version
|
||||
return v.major > min.major
|
||||
}
|
||||
|
||||
if v.minor != min.minor {
|
||||
// Same major, but different minor version
|
||||
return v.minor > min.minor
|
||||
}
|
||||
|
||||
// Same major and minor version
|
||||
return v.patch >= min.patch
|
||||
}
|
||||
|
||||
func parseServerVersion(v string) (serverVersion, error) {
|
||||
spl := strings.Split(v, ".")
|
||||
if len(spl) != 3 {
|
||||
return serverVersion{}, errors.New("wrong length")
|
||||
}
|
||||
var res [3]int
|
||||
for i := range res {
|
||||
tmp, err := strconv.Atoi(spl[i])
|
||||
if err != nil {
|
||||
return serverVersion{}, err
|
||||
}
|
||||
res[i] = tmp
|
||||
}
|
||||
return serverVersion{
|
||||
version: version{
|
||||
major: res[0],
|
||||
minor: res[1],
|
||||
patch: res[2],
|
||||
},
|
||||
}, nil
|
||||
}
|
21
vendor/github.com/1Password/connect-sdk-go/onepassword/errors.go
generated
vendored
Normal file
21
vendor/github.com/1Password/connect-sdk-go/onepassword/errors.go
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
package onepassword
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Error is an error returned by the Connect API.
|
||||
type Error struct {
|
||||
StatusCode int `json:"status"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
func (e *Error) Error() string {
|
||||
return fmt.Sprintf("status %d: %s", e.StatusCode, e.Message)
|
||||
}
|
||||
|
||||
func (e *Error) Is(target error) bool {
|
||||
t, ok := target.(*Error)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return t.Message == e.Message && t.StatusCode == e.StatusCode
|
||||
}
|
49
vendor/github.com/1Password/connect-sdk-go/onepassword/files.go
generated
vendored
Normal file
49
vendor/github.com/1Password/connect-sdk-go/onepassword/files.go
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
package onepassword
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
)
|
||||
|
||||
type File struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Section *ItemSection `json:"section,omitempty"`
|
||||
Size int `json:"size"`
|
||||
ContentPath string `json:"content_path"`
|
||||
content []byte
|
||||
}
|
||||
|
||||
func (f *File) UnmarshalJSON(data []byte) error {
|
||||
var jsonFile struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Section *ItemSection `json:"section,omitempty"`
|
||||
Size int `json:"size"`
|
||||
ContentPath string `json:"content_path"`
|
||||
Content []byte `json:"content,omitempty"`
|
||||
}
|
||||
if err := json.Unmarshal(data, &jsonFile); err != nil {
|
||||
return err
|
||||
}
|
||||
f.ID = jsonFile.ID
|
||||
f.Name = jsonFile.Name
|
||||
f.Section = jsonFile.Section
|
||||
f.Size = jsonFile.Size
|
||||
f.ContentPath = jsonFile.ContentPath
|
||||
f.content = jsonFile.Content
|
||||
return nil
|
||||
}
|
||||
|
||||
// Content returns the content of the file if they have been loaded and returns an error if they have not been loaded.
|
||||
// Use `client.GetFileContent(file *File)` instead to make sure the content is fetched automatically if not present.
|
||||
func (f *File) Content() ([]byte, error) {
|
||||
if f.content == nil {
|
||||
return nil, errors.New("file content not loaded")
|
||||
}
|
||||
return f.content, nil
|
||||
}
|
||||
|
||||
func (f *File) SetContent(content []byte) {
|
||||
f.content = content
|
||||
}
|
56
vendor/github.com/1Password/connect-sdk-go/onepassword/items.go
generated
vendored
56
vendor/github.com/1Password/connect-sdk-go/onepassword/items.go
generated
vendored
@@ -2,6 +2,7 @@ package onepassword
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -27,6 +28,7 @@ const (
|
||||
Document ItemCategory = "DOCUMENT"
|
||||
EmailAccount ItemCategory = "EMAIL_ACCOUNT"
|
||||
SocialSecurityNumber ItemCategory = "SOCIAL_SECURITY_NUMBER"
|
||||
ApiCredential ItemCategory = "API_CREDENTIAL"
|
||||
Custom ItemCategory = "CUSTOM"
|
||||
)
|
||||
|
||||
@@ -38,7 +40,7 @@ func (ic *ItemCategory) UnmarshalJSON(b []byte) error {
|
||||
switch category {
|
||||
case Login, Password, Server, Database, CreditCard, Membership, Passport, SoftwareLicense,
|
||||
OutdoorLicense, SecureNote, WirelessRouter, BankAccount, DriverLicense, Identity, RewardProgram,
|
||||
Document, EmailAccount, SocialSecurityNumber:
|
||||
Document, EmailAccount, SocialSecurityNumber, ApiCredential:
|
||||
*ic = category
|
||||
default:
|
||||
*ic = Custom
|
||||
@@ -63,6 +65,7 @@ type Item struct {
|
||||
|
||||
Sections []*ItemSection `json:"sections,omitempty"`
|
||||
Fields []*ItemField `json:"fields,omitempty"`
|
||||
Files []*File `json:"files,omitempty"`
|
||||
|
||||
LastEditedBy string `json:"lastEditedBy,omitempty"`
|
||||
CreatedAt time.Time `json:"createdAt,omitempty"`
|
||||
@@ -104,3 +107,54 @@ type ItemField struct {
|
||||
Recipe *GeneratorRecipe `json:"recipe,omitempty"`
|
||||
Entropy float64 `json:"entropy,omitempty"`
|
||||
}
|
||||
|
||||
// Get Retrieve the value of a field on the item by its label. To specify a
|
||||
// field from a specific section pass in <section label>.<field label>. If
|
||||
// no field matching the selector is found return "".
|
||||
func (i *Item) GetValue(field string) string {
|
||||
if i == nil || len(i.Fields) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
sectionFilter := false
|
||||
sectionLabel := ""
|
||||
fieldLabel := field
|
||||
if strings.Contains(field, ".") {
|
||||
parts := strings.Split(field, ".")
|
||||
|
||||
// Test to make sure the . isn't the last character
|
||||
if len(parts) == 2 {
|
||||
sectionFilter = true
|
||||
sectionLabel = parts[0]
|
||||
fieldLabel = parts[1]
|
||||
}
|
||||
}
|
||||
|
||||
for _, f := range i.Fields {
|
||||
if sectionFilter {
|
||||
if f.Section != nil {
|
||||
if sectionLabel != i.SectionLabelForID(f.Section.ID) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if fieldLabel == f.Label {
|
||||
return f.Value
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (i *Item) SectionLabelForID(id string) string {
|
||||
if i != nil || len(i.Sections) > 0 {
|
||||
for _, s := range i.Sections {
|
||||
if s.ID == id {
|
||||
return s.Label
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
172
vendor/github.com/stretchr/testify/assert/assertion_compare.go
generated
vendored
172
vendor/github.com/stretchr/testify/assert/assertion_compare.go
generated
vendored
@@ -13,12 +13,42 @@ const (
|
||||
compareGreater
|
||||
)
|
||||
|
||||
var (
|
||||
intType = reflect.TypeOf(int(1))
|
||||
int8Type = reflect.TypeOf(int8(1))
|
||||
int16Type = reflect.TypeOf(int16(1))
|
||||
int32Type = reflect.TypeOf(int32(1))
|
||||
int64Type = reflect.TypeOf(int64(1))
|
||||
|
||||
uintType = reflect.TypeOf(uint(1))
|
||||
uint8Type = reflect.TypeOf(uint8(1))
|
||||
uint16Type = reflect.TypeOf(uint16(1))
|
||||
uint32Type = reflect.TypeOf(uint32(1))
|
||||
uint64Type = reflect.TypeOf(uint64(1))
|
||||
|
||||
float32Type = reflect.TypeOf(float32(1))
|
||||
float64Type = reflect.TypeOf(float64(1))
|
||||
|
||||
stringType = reflect.TypeOf("")
|
||||
)
|
||||
|
||||
func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
|
||||
obj1Value := reflect.ValueOf(obj1)
|
||||
obj2Value := reflect.ValueOf(obj2)
|
||||
|
||||
// throughout this switch we try and avoid calling .Convert() if possible,
|
||||
// as this has a pretty big performance impact
|
||||
switch kind {
|
||||
case reflect.Int:
|
||||
{
|
||||
intobj1 := obj1.(int)
|
||||
intobj2 := obj2.(int)
|
||||
intobj1, ok := obj1.(int)
|
||||
if !ok {
|
||||
intobj1 = obj1Value.Convert(intType).Interface().(int)
|
||||
}
|
||||
intobj2, ok := obj2.(int)
|
||||
if !ok {
|
||||
intobj2 = obj2Value.Convert(intType).Interface().(int)
|
||||
}
|
||||
if intobj1 > intobj2 {
|
||||
return compareGreater, true
|
||||
}
|
||||
@@ -31,8 +61,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
|
||||
}
|
||||
case reflect.Int8:
|
||||
{
|
||||
int8obj1 := obj1.(int8)
|
||||
int8obj2 := obj2.(int8)
|
||||
int8obj1, ok := obj1.(int8)
|
||||
if !ok {
|
||||
int8obj1 = obj1Value.Convert(int8Type).Interface().(int8)
|
||||
}
|
||||
int8obj2, ok := obj2.(int8)
|
||||
if !ok {
|
||||
int8obj2 = obj2Value.Convert(int8Type).Interface().(int8)
|
||||
}
|
||||
if int8obj1 > int8obj2 {
|
||||
return compareGreater, true
|
||||
}
|
||||
@@ -45,8 +81,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
|
||||
}
|
||||
case reflect.Int16:
|
||||
{
|
||||
int16obj1 := obj1.(int16)
|
||||
int16obj2 := obj2.(int16)
|
||||
int16obj1, ok := obj1.(int16)
|
||||
if !ok {
|
||||
int16obj1 = obj1Value.Convert(int16Type).Interface().(int16)
|
||||
}
|
||||
int16obj2, ok := obj2.(int16)
|
||||
if !ok {
|
||||
int16obj2 = obj2Value.Convert(int16Type).Interface().(int16)
|
||||
}
|
||||
if int16obj1 > int16obj2 {
|
||||
return compareGreater, true
|
||||
}
|
||||
@@ -59,8 +101,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
|
||||
}
|
||||
case reflect.Int32:
|
||||
{
|
||||
int32obj1 := obj1.(int32)
|
||||
int32obj2 := obj2.(int32)
|
||||
int32obj1, ok := obj1.(int32)
|
||||
if !ok {
|
||||
int32obj1 = obj1Value.Convert(int32Type).Interface().(int32)
|
||||
}
|
||||
int32obj2, ok := obj2.(int32)
|
||||
if !ok {
|
||||
int32obj2 = obj2Value.Convert(int32Type).Interface().(int32)
|
||||
}
|
||||
if int32obj1 > int32obj2 {
|
||||
return compareGreater, true
|
||||
}
|
||||
@@ -73,8 +121,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
|
||||
}
|
||||
case reflect.Int64:
|
||||
{
|
||||
int64obj1 := obj1.(int64)
|
||||
int64obj2 := obj2.(int64)
|
||||
int64obj1, ok := obj1.(int64)
|
||||
if !ok {
|
||||
int64obj1 = obj1Value.Convert(int64Type).Interface().(int64)
|
||||
}
|
||||
int64obj2, ok := obj2.(int64)
|
||||
if !ok {
|
||||
int64obj2 = obj2Value.Convert(int64Type).Interface().(int64)
|
||||
}
|
||||
if int64obj1 > int64obj2 {
|
||||
return compareGreater, true
|
||||
}
|
||||
@@ -87,8 +141,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
|
||||
}
|
||||
case reflect.Uint:
|
||||
{
|
||||
uintobj1 := obj1.(uint)
|
||||
uintobj2 := obj2.(uint)
|
||||
uintobj1, ok := obj1.(uint)
|
||||
if !ok {
|
||||
uintobj1 = obj1Value.Convert(uintType).Interface().(uint)
|
||||
}
|
||||
uintobj2, ok := obj2.(uint)
|
||||
if !ok {
|
||||
uintobj2 = obj2Value.Convert(uintType).Interface().(uint)
|
||||
}
|
||||
if uintobj1 > uintobj2 {
|
||||
return compareGreater, true
|
||||
}
|
||||
@@ -101,8 +161,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
|
||||
}
|
||||
case reflect.Uint8:
|
||||
{
|
||||
uint8obj1 := obj1.(uint8)
|
||||
uint8obj2 := obj2.(uint8)
|
||||
uint8obj1, ok := obj1.(uint8)
|
||||
if !ok {
|
||||
uint8obj1 = obj1Value.Convert(uint8Type).Interface().(uint8)
|
||||
}
|
||||
uint8obj2, ok := obj2.(uint8)
|
||||
if !ok {
|
||||
uint8obj2 = obj2Value.Convert(uint8Type).Interface().(uint8)
|
||||
}
|
||||
if uint8obj1 > uint8obj2 {
|
||||
return compareGreater, true
|
||||
}
|
||||
@@ -115,8 +181,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
|
||||
}
|
||||
case reflect.Uint16:
|
||||
{
|
||||
uint16obj1 := obj1.(uint16)
|
||||
uint16obj2 := obj2.(uint16)
|
||||
uint16obj1, ok := obj1.(uint16)
|
||||
if !ok {
|
||||
uint16obj1 = obj1Value.Convert(uint16Type).Interface().(uint16)
|
||||
}
|
||||
uint16obj2, ok := obj2.(uint16)
|
||||
if !ok {
|
||||
uint16obj2 = obj2Value.Convert(uint16Type).Interface().(uint16)
|
||||
}
|
||||
if uint16obj1 > uint16obj2 {
|
||||
return compareGreater, true
|
||||
}
|
||||
@@ -129,8 +201,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
|
||||
}
|
||||
case reflect.Uint32:
|
||||
{
|
||||
uint32obj1 := obj1.(uint32)
|
||||
uint32obj2 := obj2.(uint32)
|
||||
uint32obj1, ok := obj1.(uint32)
|
||||
if !ok {
|
||||
uint32obj1 = obj1Value.Convert(uint32Type).Interface().(uint32)
|
||||
}
|
||||
uint32obj2, ok := obj2.(uint32)
|
||||
if !ok {
|
||||
uint32obj2 = obj2Value.Convert(uint32Type).Interface().(uint32)
|
||||
}
|
||||
if uint32obj1 > uint32obj2 {
|
||||
return compareGreater, true
|
||||
}
|
||||
@@ -143,8 +221,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
|
||||
}
|
||||
case reflect.Uint64:
|
||||
{
|
||||
uint64obj1 := obj1.(uint64)
|
||||
uint64obj2 := obj2.(uint64)
|
||||
uint64obj1, ok := obj1.(uint64)
|
||||
if !ok {
|
||||
uint64obj1 = obj1Value.Convert(uint64Type).Interface().(uint64)
|
||||
}
|
||||
uint64obj2, ok := obj2.(uint64)
|
||||
if !ok {
|
||||
uint64obj2 = obj2Value.Convert(uint64Type).Interface().(uint64)
|
||||
}
|
||||
if uint64obj1 > uint64obj2 {
|
||||
return compareGreater, true
|
||||
}
|
||||
@@ -157,8 +241,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
|
||||
}
|
||||
case reflect.Float32:
|
||||
{
|
||||
float32obj1 := obj1.(float32)
|
||||
float32obj2 := obj2.(float32)
|
||||
float32obj1, ok := obj1.(float32)
|
||||
if !ok {
|
||||
float32obj1 = obj1Value.Convert(float32Type).Interface().(float32)
|
||||
}
|
||||
float32obj2, ok := obj2.(float32)
|
||||
if !ok {
|
||||
float32obj2 = obj2Value.Convert(float32Type).Interface().(float32)
|
||||
}
|
||||
if float32obj1 > float32obj2 {
|
||||
return compareGreater, true
|
||||
}
|
||||
@@ -171,8 +261,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
|
||||
}
|
||||
case reflect.Float64:
|
||||
{
|
||||
float64obj1 := obj1.(float64)
|
||||
float64obj2 := obj2.(float64)
|
||||
float64obj1, ok := obj1.(float64)
|
||||
if !ok {
|
||||
float64obj1 = obj1Value.Convert(float64Type).Interface().(float64)
|
||||
}
|
||||
float64obj2, ok := obj2.(float64)
|
||||
if !ok {
|
||||
float64obj2 = obj2Value.Convert(float64Type).Interface().(float64)
|
||||
}
|
||||
if float64obj1 > float64obj2 {
|
||||
return compareGreater, true
|
||||
}
|
||||
@@ -185,8 +281,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
|
||||
}
|
||||
case reflect.String:
|
||||
{
|
||||
stringobj1 := obj1.(string)
|
||||
stringobj2 := obj2.(string)
|
||||
stringobj1, ok := obj1.(string)
|
||||
if !ok {
|
||||
stringobj1 = obj1Value.Convert(stringType).Interface().(string)
|
||||
}
|
||||
stringobj2, ok := obj2.(string)
|
||||
if !ok {
|
||||
stringobj2 = obj2Value.Convert(stringType).Interface().(string)
|
||||
}
|
||||
if stringobj1 > stringobj2 {
|
||||
return compareGreater, true
|
||||
}
|
||||
@@ -240,6 +342,24 @@ func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...inter
|
||||
return compareTwoValues(t, e1, e2, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs)
|
||||
}
|
||||
|
||||
// Positive asserts that the specified element is positive
|
||||
//
|
||||
// assert.Positive(t, 1)
|
||||
// assert.Positive(t, 1.23)
|
||||
func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) bool {
|
||||
zero := reflect.Zero(reflect.TypeOf(e))
|
||||
return compareTwoValues(t, e, zero.Interface(), []CompareType{compareGreater}, "\"%v\" is not positive", msgAndArgs)
|
||||
}
|
||||
|
||||
// Negative asserts that the specified element is negative
|
||||
//
|
||||
// assert.Negative(t, -1)
|
||||
// assert.Negative(t, -1.23)
|
||||
func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) bool {
|
||||
zero := reflect.Zero(reflect.TypeOf(e))
|
||||
return compareTwoValues(t, e, zero.Interface(), []CompareType{compareLess}, "\"%v\" is not negative", msgAndArgs)
|
||||
}
|
||||
|
||||
func compareTwoValues(t TestingT, e1 interface{}, e2 interface{}, allowedComparesResults []CompareType, failMessage string, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
|
97
vendor/github.com/stretchr/testify/assert/assertion_format.go
generated
vendored
97
vendor/github.com/stretchr/testify/assert/assertion_format.go
generated
vendored
@@ -114,6 +114,24 @@ func Errorf(t TestingT, err error, msg string, args ...interface{}) bool {
|
||||
return Error(t, err, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// ErrorAsf asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value.
|
||||
// This is a wrapper for errors.As.
|
||||
func ErrorAsf(t TestingT, err error, target interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return ErrorAs(t, err, target, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// ErrorIsf asserts that at least one of the errors in err's chain matches target.
|
||||
// This is a wrapper for errors.Is.
|
||||
func ErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return ErrorIs(t, err, target, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// Eventuallyf asserts that given condition will be met in waitFor time,
|
||||
// periodically checking target function each tick.
|
||||
//
|
||||
@@ -321,6 +339,54 @@ func InEpsilonSlicef(t TestingT, expected interface{}, actual interface{}, epsil
|
||||
return InEpsilonSlice(t, expected, actual, epsilon, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// IsDecreasingf asserts that the collection is decreasing
|
||||
//
|
||||
// assert.IsDecreasingf(t, []int{2, 1, 0}, "error message %s", "formatted")
|
||||
// assert.IsDecreasingf(t, []float{2, 1}, "error message %s", "formatted")
|
||||
// assert.IsDecreasingf(t, []string{"b", "a"}, "error message %s", "formatted")
|
||||
func IsDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return IsDecreasing(t, object, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// IsIncreasingf asserts that the collection is increasing
|
||||
//
|
||||
// assert.IsIncreasingf(t, []int{1, 2, 3}, "error message %s", "formatted")
|
||||
// assert.IsIncreasingf(t, []float{1, 2}, "error message %s", "formatted")
|
||||
// assert.IsIncreasingf(t, []string{"a", "b"}, "error message %s", "formatted")
|
||||
func IsIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return IsIncreasing(t, object, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// IsNonDecreasingf asserts that the collection is not decreasing
|
||||
//
|
||||
// assert.IsNonDecreasingf(t, []int{1, 1, 2}, "error message %s", "formatted")
|
||||
// assert.IsNonDecreasingf(t, []float{1, 2}, "error message %s", "formatted")
|
||||
// assert.IsNonDecreasingf(t, []string{"a", "b"}, "error message %s", "formatted")
|
||||
func IsNonDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return IsNonDecreasing(t, object, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// IsNonIncreasingf asserts that the collection is not increasing
|
||||
//
|
||||
// assert.IsNonIncreasingf(t, []int{2, 1, 1}, "error message %s", "formatted")
|
||||
// assert.IsNonIncreasingf(t, []float{2, 1}, "error message %s", "formatted")
|
||||
// assert.IsNonIncreasingf(t, []string{"b", "a"}, "error message %s", "formatted")
|
||||
func IsNonIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return IsNonIncreasing(t, object, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// IsTypef asserts that the specified objects are of the same type.
|
||||
func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
@@ -375,6 +441,17 @@ func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args .
|
||||
return LessOrEqual(t, e1, e2, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// Negativef asserts that the specified element is negative
|
||||
//
|
||||
// assert.Negativef(t, -1, "error message %s", "formatted")
|
||||
// assert.Negativef(t, -1.23, "error message %s", "formatted")
|
||||
func Negativef(t TestingT, e interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return Negative(t, e, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// Neverf asserts that the given condition doesn't satisfy in waitFor time,
|
||||
// periodically checking the target function each tick.
|
||||
//
|
||||
@@ -476,6 +553,15 @@ func NotEqualValuesf(t TestingT, expected interface{}, actual interface{}, msg s
|
||||
return NotEqualValues(t, expected, actual, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// NotErrorIsf asserts that at none of the errors in err's chain matches target.
|
||||
// This is a wrapper for errors.Is.
|
||||
func NotErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return NotErrorIs(t, err, target, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// NotNilf asserts that the specified object is not nil.
|
||||
//
|
||||
// assert.NotNilf(t, err, "error message %s", "formatted")
|
||||
@@ -572,6 +658,17 @@ func PanicsWithValuef(t TestingT, expected interface{}, f PanicTestFunc, msg str
|
||||
return PanicsWithValue(t, expected, f, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// Positivef asserts that the specified element is positive
|
||||
//
|
||||
// assert.Positivef(t, 1, "error message %s", "formatted")
|
||||
// assert.Positivef(t, 1.23, "error message %s", "formatted")
|
||||
func Positivef(t TestingT, e interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return Positive(t, e, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// Regexpf asserts that a specified regexp matches a string.
|
||||
//
|
||||
// assert.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted")
|
||||
|
194
vendor/github.com/stretchr/testify/assert/assertion_forward.go
generated
vendored
194
vendor/github.com/stretchr/testify/assert/assertion_forward.go
generated
vendored
@@ -204,6 +204,42 @@ func (a *Assertions) Error(err error, msgAndArgs ...interface{}) bool {
|
||||
return Error(a.t, err, msgAndArgs...)
|
||||
}
|
||||
|
||||
// ErrorAs asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value.
|
||||
// This is a wrapper for errors.As.
|
||||
func (a *Assertions) ErrorAs(err error, target interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return ErrorAs(a.t, err, target, msgAndArgs...)
|
||||
}
|
||||
|
||||
// ErrorAsf asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value.
|
||||
// This is a wrapper for errors.As.
|
||||
func (a *Assertions) ErrorAsf(err error, target interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return ErrorAsf(a.t, err, target, msg, args...)
|
||||
}
|
||||
|
||||
// ErrorIs asserts that at least one of the errors in err's chain matches target.
|
||||
// This is a wrapper for errors.Is.
|
||||
func (a *Assertions) ErrorIs(err error, target error, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return ErrorIs(a.t, err, target, msgAndArgs...)
|
||||
}
|
||||
|
||||
// ErrorIsf asserts that at least one of the errors in err's chain matches target.
|
||||
// This is a wrapper for errors.Is.
|
||||
func (a *Assertions) ErrorIsf(err error, target error, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return ErrorIsf(a.t, err, target, msg, args...)
|
||||
}
|
||||
|
||||
// Errorf asserts that a function returned an error (i.e. not `nil`).
|
||||
//
|
||||
// actualObj, err := SomeFunction()
|
||||
@@ -631,6 +667,102 @@ func (a *Assertions) InEpsilonf(expected interface{}, actual interface{}, epsilo
|
||||
return InEpsilonf(a.t, expected, actual, epsilon, msg, args...)
|
||||
}
|
||||
|
||||
// IsDecreasing asserts that the collection is decreasing
|
||||
//
|
||||
// a.IsDecreasing([]int{2, 1, 0})
|
||||
// a.IsDecreasing([]float{2, 1})
|
||||
// a.IsDecreasing([]string{"b", "a"})
|
||||
func (a *Assertions) IsDecreasing(object interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return IsDecreasing(a.t, object, msgAndArgs...)
|
||||
}
|
||||
|
||||
// IsDecreasingf asserts that the collection is decreasing
|
||||
//
|
||||
// a.IsDecreasingf([]int{2, 1, 0}, "error message %s", "formatted")
|
||||
// a.IsDecreasingf([]float{2, 1}, "error message %s", "formatted")
|
||||
// a.IsDecreasingf([]string{"b", "a"}, "error message %s", "formatted")
|
||||
func (a *Assertions) IsDecreasingf(object interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return IsDecreasingf(a.t, object, msg, args...)
|
||||
}
|
||||
|
||||
// IsIncreasing asserts that the collection is increasing
|
||||
//
|
||||
// a.IsIncreasing([]int{1, 2, 3})
|
||||
// a.IsIncreasing([]float{1, 2})
|
||||
// a.IsIncreasing([]string{"a", "b"})
|
||||
func (a *Assertions) IsIncreasing(object interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return IsIncreasing(a.t, object, msgAndArgs...)
|
||||
}
|
||||
|
||||
// IsIncreasingf asserts that the collection is increasing
|
||||
//
|
||||
// a.IsIncreasingf([]int{1, 2, 3}, "error message %s", "formatted")
|
||||
// a.IsIncreasingf([]float{1, 2}, "error message %s", "formatted")
|
||||
// a.IsIncreasingf([]string{"a", "b"}, "error message %s", "formatted")
|
||||
func (a *Assertions) IsIncreasingf(object interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return IsIncreasingf(a.t, object, msg, args...)
|
||||
}
|
||||
|
||||
// IsNonDecreasing asserts that the collection is not decreasing
|
||||
//
|
||||
// a.IsNonDecreasing([]int{1, 1, 2})
|
||||
// a.IsNonDecreasing([]float{1, 2})
|
||||
// a.IsNonDecreasing([]string{"a", "b"})
|
||||
func (a *Assertions) IsNonDecreasing(object interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return IsNonDecreasing(a.t, object, msgAndArgs...)
|
||||
}
|
||||
|
||||
// IsNonDecreasingf asserts that the collection is not decreasing
|
||||
//
|
||||
// a.IsNonDecreasingf([]int{1, 1, 2}, "error message %s", "formatted")
|
||||
// a.IsNonDecreasingf([]float{1, 2}, "error message %s", "formatted")
|
||||
// a.IsNonDecreasingf([]string{"a", "b"}, "error message %s", "formatted")
|
||||
func (a *Assertions) IsNonDecreasingf(object interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return IsNonDecreasingf(a.t, object, msg, args...)
|
||||
}
|
||||
|
||||
// IsNonIncreasing asserts that the collection is not increasing
|
||||
//
|
||||
// a.IsNonIncreasing([]int{2, 1, 1})
|
||||
// a.IsNonIncreasing([]float{2, 1})
|
||||
// a.IsNonIncreasing([]string{"b", "a"})
|
||||
func (a *Assertions) IsNonIncreasing(object interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return IsNonIncreasing(a.t, object, msgAndArgs...)
|
||||
}
|
||||
|
||||
// IsNonIncreasingf asserts that the collection is not increasing
|
||||
//
|
||||
// a.IsNonIncreasingf([]int{2, 1, 1}, "error message %s", "formatted")
|
||||
// a.IsNonIncreasingf([]float{2, 1}, "error message %s", "formatted")
|
||||
// a.IsNonIncreasingf([]string{"b", "a"}, "error message %s", "formatted")
|
||||
func (a *Assertions) IsNonIncreasingf(object interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return IsNonIncreasingf(a.t, object, msg, args...)
|
||||
}
|
||||
|
||||
// IsType asserts that the specified objects are of the same type.
|
||||
func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
@@ -739,6 +871,28 @@ func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...i
|
||||
return Lessf(a.t, e1, e2, msg, args...)
|
||||
}
|
||||
|
||||
// Negative asserts that the specified element is negative
|
||||
//
|
||||
// a.Negative(-1)
|
||||
// a.Negative(-1.23)
|
||||
func (a *Assertions) Negative(e interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return Negative(a.t, e, msgAndArgs...)
|
||||
}
|
||||
|
||||
// Negativef asserts that the specified element is negative
|
||||
//
|
||||
// a.Negativef(-1, "error message %s", "formatted")
|
||||
// a.Negativef(-1.23, "error message %s", "formatted")
|
||||
func (a *Assertions) Negativef(e interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return Negativef(a.t, e, msg, args...)
|
||||
}
|
||||
|
||||
// Never asserts that the given condition doesn't satisfy in waitFor time,
|
||||
// periodically checking the target function each tick.
|
||||
//
|
||||
@@ -941,6 +1095,24 @@ func (a *Assertions) NotEqualf(expected interface{}, actual interface{}, msg str
|
||||
return NotEqualf(a.t, expected, actual, msg, args...)
|
||||
}
|
||||
|
||||
// NotErrorIs asserts that at none of the errors in err's chain matches target.
|
||||
// This is a wrapper for errors.Is.
|
||||
func (a *Assertions) NotErrorIs(err error, target error, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return NotErrorIs(a.t, err, target, msgAndArgs...)
|
||||
}
|
||||
|
||||
// NotErrorIsf asserts that at none of the errors in err's chain matches target.
|
||||
// This is a wrapper for errors.Is.
|
||||
func (a *Assertions) NotErrorIsf(err error, target error, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return NotErrorIsf(a.t, err, target, msg, args...)
|
||||
}
|
||||
|
||||
// NotNil asserts that the specified object is not nil.
|
||||
//
|
||||
// a.NotNil(err)
|
||||
@@ -1133,6 +1305,28 @@ func (a *Assertions) Panicsf(f PanicTestFunc, msg string, args ...interface{}) b
|
||||
return Panicsf(a.t, f, msg, args...)
|
||||
}
|
||||
|
||||
// Positive asserts that the specified element is positive
|
||||
//
|
||||
// a.Positive(1)
|
||||
// a.Positive(1.23)
|
||||
func (a *Assertions) Positive(e interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return Positive(a.t, e, msgAndArgs...)
|
||||
}
|
||||
|
||||
// Positivef asserts that the specified element is positive
|
||||
//
|
||||
// a.Positivef(1, "error message %s", "formatted")
|
||||
// a.Positivef(1.23, "error message %s", "formatted")
|
||||
func (a *Assertions) Positivef(e interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return Positivef(a.t, e, msg, args...)
|
||||
}
|
||||
|
||||
// Regexp asserts that a specified regexp matches a string.
|
||||
//
|
||||
// a.Regexp(regexp.MustCompile("start"), "it's starting")
|
||||
|
81
vendor/github.com/stretchr/testify/assert/assertion_order.go
generated
vendored
Normal file
81
vendor/github.com/stretchr/testify/assert/assertion_order.go
generated
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
package assert
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// isOrdered checks that collection contains orderable elements.
|
||||
func isOrdered(t TestingT, object interface{}, allowedComparesResults []CompareType, failMessage string, msgAndArgs ...interface{}) bool {
|
||||
objKind := reflect.TypeOf(object).Kind()
|
||||
if objKind != reflect.Slice && objKind != reflect.Array {
|
||||
return false
|
||||
}
|
||||
|
||||
objValue := reflect.ValueOf(object)
|
||||
objLen := objValue.Len()
|
||||
|
||||
if objLen <= 1 {
|
||||
return true
|
||||
}
|
||||
|
||||
value := objValue.Index(0)
|
||||
valueInterface := value.Interface()
|
||||
firstValueKind := value.Kind()
|
||||
|
||||
for i := 1; i < objLen; i++ {
|
||||
prevValue := value
|
||||
prevValueInterface := valueInterface
|
||||
|
||||
value = objValue.Index(i)
|
||||
valueInterface = value.Interface()
|
||||
|
||||
compareResult, isComparable := compare(prevValueInterface, valueInterface, firstValueKind)
|
||||
|
||||
if !isComparable {
|
||||
return Fail(t, fmt.Sprintf("Can not compare type \"%s\" and \"%s\"", reflect.TypeOf(value), reflect.TypeOf(prevValue)), msgAndArgs...)
|
||||
}
|
||||
|
||||
if !containsValue(allowedComparesResults, compareResult) {
|
||||
return Fail(t, fmt.Sprintf(failMessage, prevValue, value), msgAndArgs...)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// IsIncreasing asserts that the collection is increasing
|
||||
//
|
||||
// assert.IsIncreasing(t, []int{1, 2, 3})
|
||||
// assert.IsIncreasing(t, []float{1, 2})
|
||||
// assert.IsIncreasing(t, []string{"a", "b"})
|
||||
func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
return isOrdered(t, object, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs)
|
||||
}
|
||||
|
||||
// IsNonIncreasing asserts that the collection is not increasing
|
||||
//
|
||||
// assert.IsNonIncreasing(t, []int{2, 1, 1})
|
||||
// assert.IsNonIncreasing(t, []float{2, 1})
|
||||
// assert.IsNonIncreasing(t, []string{"b", "a"})
|
||||
func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
return isOrdered(t, object, []CompareType{compareEqual, compareGreater}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs)
|
||||
}
|
||||
|
||||
// IsDecreasing asserts that the collection is decreasing
|
||||
//
|
||||
// assert.IsDecreasing(t, []int{2, 1, 0})
|
||||
// assert.IsDecreasing(t, []float{2, 1})
|
||||
// assert.IsDecreasing(t, []string{"b", "a"})
|
||||
func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
return isOrdered(t, object, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs)
|
||||
}
|
||||
|
||||
// IsNonDecreasing asserts that the collection is not decreasing
|
||||
//
|
||||
// assert.IsNonDecreasing(t, []int{1, 1, 2})
|
||||
// assert.IsNonDecreasing(t, []float{1, 2})
|
||||
// assert.IsNonDecreasing(t, []string{"a", "b"})
|
||||
func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
return isOrdered(t, object, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs)
|
||||
}
|
83
vendor/github.com/stretchr/testify/assert/assertions.go
generated
vendored
83
vendor/github.com/stretchr/testify/assert/assertions.go
generated
vendored
@@ -172,8 +172,8 @@ func isTest(name, prefix string) bool {
|
||||
if len(name) == len(prefix) { // "Test" is ok
|
||||
return true
|
||||
}
|
||||
rune, _ := utf8.DecodeRuneInString(name[len(prefix):])
|
||||
return !unicode.IsLower(rune)
|
||||
r, _ := utf8.DecodeRuneInString(name[len(prefix):])
|
||||
return !unicode.IsLower(r)
|
||||
}
|
||||
|
||||
func messageFromMsgAndArgs(msgAndArgs ...interface{}) string {
|
||||
@@ -1622,6 +1622,7 @@ var spewConfig = spew.ConfigState{
|
||||
DisableCapacities: true,
|
||||
SortKeys: true,
|
||||
DisableMethods: true,
|
||||
MaxDepth: 10,
|
||||
}
|
||||
|
||||
type tHelper interface {
|
||||
@@ -1693,3 +1694,81 @@ func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.D
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ErrorIs asserts that at least one of the errors in err's chain matches target.
|
||||
// This is a wrapper for errors.Is.
|
||||
func ErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
if errors.Is(err, target) {
|
||||
return true
|
||||
}
|
||||
|
||||
var expectedText string
|
||||
if target != nil {
|
||||
expectedText = target.Error()
|
||||
}
|
||||
|
||||
chain := buildErrorChainString(err)
|
||||
|
||||
return Fail(t, fmt.Sprintf("Target error should be in err chain:\n"+
|
||||
"expected: %q\n"+
|
||||
"in chain: %s", expectedText, chain,
|
||||
), msgAndArgs...)
|
||||
}
|
||||
|
||||
// NotErrorIs asserts that at none of the errors in err's chain matches target.
|
||||
// This is a wrapper for errors.Is.
|
||||
func NotErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
if !errors.Is(err, target) {
|
||||
return true
|
||||
}
|
||||
|
||||
var expectedText string
|
||||
if target != nil {
|
||||
expectedText = target.Error()
|
||||
}
|
||||
|
||||
chain := buildErrorChainString(err)
|
||||
|
||||
return Fail(t, fmt.Sprintf("Target error should not be in err chain:\n"+
|
||||
"found: %q\n"+
|
||||
"in chain: %s", expectedText, chain,
|
||||
), msgAndArgs...)
|
||||
}
|
||||
|
||||
// ErrorAs asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value.
|
||||
// This is a wrapper for errors.As.
|
||||
func ErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
if errors.As(err, target) {
|
||||
return true
|
||||
}
|
||||
|
||||
chain := buildErrorChainString(err)
|
||||
|
||||
return Fail(t, fmt.Sprintf("Should be in error chain:\n"+
|
||||
"expected: %q\n"+
|
||||
"in chain: %s", target, chain,
|
||||
), msgAndArgs...)
|
||||
}
|
||||
|
||||
func buildErrorChainString(err error) string {
|
||||
if err == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
e := errors.Unwrap(err)
|
||||
chain := fmt.Sprintf("%q", err.Error())
|
||||
for e != nil {
|
||||
chain += fmt.Sprintf("\n\t%q", e.Error())
|
||||
e = errors.Unwrap(e)
|
||||
}
|
||||
return chain
|
||||
}
|
||||
|
4
vendor/modules.txt
vendored
4
vendor/modules.txt
vendored
@@ -1,6 +1,6 @@
|
||||
# cloud.google.com/go v0.49.0
|
||||
cloud.google.com/go/compute/metadata
|
||||
# github.com/1Password/connect-sdk-go v0.0.2
|
||||
# github.com/1Password/connect-sdk-go v1.2.0
|
||||
github.com/1Password/connect-sdk-go/connect
|
||||
github.com/1Password/connect-sdk-go/onepassword
|
||||
# github.com/Azure/go-autorest/autorest v0.9.3-0.20191028180845-3492b2aff503
|
||||
@@ -110,7 +110,7 @@ github.com/prometheus/procfs/internal/fs
|
||||
github.com/prometheus/procfs/internal/util
|
||||
# github.com/spf13/pflag v1.0.5
|
||||
github.com/spf13/pflag
|
||||
# github.com/stretchr/testify v1.6.1
|
||||
# github.com/stretchr/testify v1.7.0
|
||||
github.com/stretchr/testify/assert
|
||||
# github.com/uber/jaeger-client-go v2.25.0+incompatible
|
||||
github.com/uber/jaeger-client-go
|
||||
|
Reference in New Issue
Block a user