├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ └── go-portieris.yaml ├── .gitignore ├── .nancy-ignore ├── .pre-commit-config.yaml ├── .secrets.baseline ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── Makefile ├── POLICIES.md ├── README.md ├── RELEASE-HOWTO.md ├── cmd └── portieris │ └── main.go ├── files-amd64.txt ├── files-arm64.txt ├── files-s390x.txt ├── go.mod ├── go.sum ├── helm ├── cleanup.sh └── portieris │ ├── .helmignore │ ├── Chart.yaml │ ├── LICENSE │ ├── README.md │ ├── crds │ └── crds.yaml │ ├── gencerts │ ├── templates │ ├── NOTES.txt │ ├── _helpers.tpl │ ├── clusterrole.yaml │ ├── clusterrolebinding.yaml │ ├── deployment.yaml │ ├── namespace.yaml │ ├── pdb.yaml │ ├── policies.yaml │ ├── secret.yaml │ ├── securitycontextconstraint.yaml │ ├── service.yaml │ ├── serviceaccount.yaml │ └── webhooks.yaml │ └── values.yaml ├── helpers ├── credential │ └── credential.go ├── http │ ├── http.go │ └── http_test.go ├── image │ ├── image.go │ └── image_test.go ├── kube │ └── kube.go ├── oauth │ ├── oauth.go │ └── token.go ├── trustmap │ └── trust_server_map.go ├── useragent │ ├── useragent.go │ └── useragent_test.go └── wildcard │ ├── wildcard.go │ └── wildcard_test.go ├── internal └── info │ └── info.go ├── logos ├── logo_only.svg ├── text_and_logo.svg └── text_only.svg ├── pkg ├── apis │ ├── README.md │ └── portieris.cloud.ibm.com │ │ ├── client │ │ ├── clientset │ │ │ └── versioned │ │ │ │ ├── clientset.go │ │ │ │ ├── doc.go │ │ │ │ ├── fake │ │ │ │ ├── clientset_generated.go │ │ │ │ ├── doc.go │ │ │ │ └── register.go │ │ │ │ ├── scheme │ │ │ │ ├── doc.go │ │ │ │ └── register.go │ │ │ │ └── typed │ │ │ │ └── portieris.cloud.ibm.com │ │ │ │ └── v1 │ │ │ │ ├── clusterimagepolicy.go │ │ │ │ ├── doc.go │ │ │ │ ├── fake │ │ │ │ ├── doc.go │ │ │ │ ├── fake_clusterimagepolicy.go │ │ │ │ ├── fake_imagepolicy.go │ │ │ │ └── fake_portieris.cloud.ibm.com_client.go │ │ │ │ ├── generated_expansion.go │ │ │ │ ├── imagepolicy.go │ │ │ │ └── portieris.cloud.ibm.com_client.go │ │ ├── informers │ │ │ └── externalversions │ │ │ │ ├── factory.go │ │ │ │ ├── generic.go │ │ │ │ ├── internalinterfaces │ │ │ │ └── factory_interfaces.go │ │ │ │ └── portieris.cloud.ibm.com │ │ │ │ ├── interface.go │ │ │ │ └── v1 │ │ │ │ ├── clusterimagepolicy.go │ │ │ │ ├── imagepolicy.go │ │ │ │ └── interface.go │ │ └── listers │ │ │ └── portieris.cloud.ibm.com │ │ │ └── v1 │ │ │ ├── clusterimagepolicy.go │ │ │ ├── expansion_generated.go │ │ │ └── imagepolicy.go │ │ ├── register.go │ │ └── v1 │ │ ├── doc.go │ │ ├── register.go │ │ ├── types.go │ │ ├── types_test.go │ │ └── zz_generated.deepcopy.go ├── controller │ ├── fakecontroller │ │ └── controller.go │ ├── interface.go │ └── multi │ │ ├── controller.go │ │ ├── controller_test.go │ │ ├── enforcer.go │ │ ├── enforcer_test.go │ │ ├── notary_suite_test.go │ │ └── notary_test.go ├── kubernetes │ ├── podspec.go │ ├── podspec_test.go │ ├── secret.go │ ├── secret_test.go │ └── wrapper.go ├── metrics │ ├── metrics.go │ └── metrics_test.go ├── notary │ ├── fakenotary │ │ ├── fakenotary.go │ │ └── fakerepository.go │ ├── notary.go │ ├── notary_suite_test.go │ └── notary_test.go ├── policy │ ├── policy.go │ └── policy_test.go ├── registry │ ├── fakeregistry │ │ └── fakeregistry.go │ └── registry.go ├── verifier │ ├── simple │ │ ├── gpgArmour.go │ │ ├── gpgArmour_test.go │ │ ├── imagePolicy.go │ │ ├── imagePolicy_test.go │ │ ├── policy.go │ │ ├── policy_test.go │ │ ├── sigStoreConfig.go │ │ ├── sigStoreConfig_test.go │ │ └── verifier.go │ ├── trust │ │ ├── notary_suite_test.go │ │ ├── trust.go │ │ ├── trust_test.go │ │ └── verifier.go │ └── vulnerability │ │ ├── iam.go │ │ ├── iam_test.go │ │ ├── ibm_va.go │ │ ├── ibm_va_test.go │ │ ├── vulnerability.go │ │ └── vulnerability_test.go └── webhook │ ├── responder.go │ ├── responder_test.go │ ├── webhook.go │ └── webhook_test.go ├── scripts ├── copyright-check.sh ├── copyright.sh ├── env.sh ├── install-on-docker ├── makeKeySecret.sh ├── makeTest.sh └── uninstall-on-docker ├── test ├── e2e │ ├── README.md │ ├── ibmcontainerservice.helm_test.go │ ├── main.go │ ├── main_test.go │ ├── notary.ibm.clusterimagepolicy_test.go │ ├── notary.ibm.imagepolicy_test.go │ ├── policy.generic_test.go │ ├── simple.clusterimagepolicy_test.go │ ├── simple.imagePolicy_test.go │ ├── testdata │ │ ├── clusterimagepolicy │ │ │ ├── allow-all.yaml │ │ │ ├── allow-signed-dockerhub.yaml │ │ │ ├── allow-signed.yaml │ │ │ ├── simple-accept-anything.yaml │ │ │ ├── simple-remap.yaml │ │ │ ├── simple-signedby1-keysecret-namespace-override.yaml │ │ │ ├── simple-signedby1.yaml │ │ │ ├── simple-signedby2-mutate.yaml │ │ │ ├── simple-signedby2-nomutate.yaml │ │ │ └── simple-signedby2.yaml │ │ ├── cronjob │ │ │ ├── global-nginx-signed.yaml │ │ │ └── global-nginx-unsigned.yaml │ │ ├── daemonset │ │ │ ├── global-nginx-signed.yaml │ │ │ └── global-nginx-unsigned.yaml │ │ ├── deployment │ │ │ ├── dockerhub-nginx-unsigned.yaml │ │ │ ├── global-nginx-another.yaml │ │ │ ├── global-nginx-multisigned.yaml │ │ │ ├── global-nginx-remapped.yaml │ │ │ ├── global-nginx-signed-free.yaml │ │ │ ├── global-nginx-signed-signed.yaml │ │ │ ├── global-nginx-signed-unsigned.yaml │ │ │ ├── global-nginx-signed.yaml │ │ │ ├── global-nginx-unsigned.yaml │ │ │ ├── global-signed-patch-to-unsigned.yaml │ │ │ ├── vulnerability-account-exemption.yaml │ │ │ ├── vulnerability-allow.yaml │ │ │ ├── vulnerability-deny-vulnerable-signed.yaml │ │ │ └── vulnerability-deny.yaml │ │ ├── imagepolicy │ │ │ ├── allow-all.yaml │ │ │ ├── allow-signed-custom.yaml │ │ │ ├── allow-signed-dockerhub.yaml │ │ │ ├── allow-signed.yaml │ │ │ ├── allow-unsigned-embedded-trailing-wildcard.yaml │ │ │ ├── allow-unsigned-embedded-wildcard.yaml │ │ │ ├── allow-unsigned-trailing-wildcard.yaml │ │ │ ├── pinned-multi.yaml │ │ │ ├── pinned-signer1.yaml │ │ │ ├── pinned-signer2.yaml │ │ │ ├── simple-accept-anything.yaml │ │ │ ├── simple-remap.yaml │ │ │ ├── simple-signedby1-keysecret-namespace-override.yaml │ │ │ ├── simple-signedby1.yaml │ │ │ ├── simple-signedby2-mutate.yaml │ │ │ ├── simple-signedby2-nomutate.yaml │ │ │ ├── simple-signedby2.yaml │ │ │ ├── vulnerability-enabled-with-simple.yaml │ │ │ ├── vulnerability-enabled.yaml │ │ │ └── vulnerability-with-account.yaml │ │ ├── job │ │ │ ├── global-nginx-signed.yaml │ │ │ └── global-nginx-unsigned.yaml │ │ ├── pod │ │ │ ├── global-nginx-signed.yaml │ │ │ └── global-nginx-unsigned.yaml │ │ ├── replicaset │ │ │ ├── global-nginx-signed.yaml │ │ │ └── global-nginx-unsigned.yaml │ │ ├── replicationcontroller │ │ │ ├── global-nginx-signed.yaml │ │ │ └── global-nginx-unsigned.yaml │ │ ├── secret │ │ │ ├── sh.pubkey.yaml │ │ │ ├── signer1.pubkey.yaml │ │ │ ├── signer2.pubkey.yaml │ │ │ ├── simple1pubkey.yaml │ │ │ └── simple2pubkey.yaml │ │ └── statefulset │ │ │ ├── global-nginx-signed.yaml │ │ │ └── global-nginx-unsigned.yaml │ ├── utils │ │ ├── README.md │ │ ├── clusterimagepolicy.go │ │ ├── cronjob.go │ │ ├── daemonset.go │ │ ├── debug.go │ │ ├── deployment.go │ │ ├── imagepolicy.go │ │ ├── job.go │ │ ├── pod.go │ │ ├── replicaset.go │ │ ├── replicationcontroller.go │ │ ├── secret.go │ │ └── statefulset.go │ └── wildcard.generic_test.go ├── framework │ ├── README.md │ ├── clusterimagepolicy.go │ ├── clusterrole.go │ ├── clusterrolebinding.go │ ├── configmap.go │ ├── cronjob.go │ ├── daemonsets.go │ ├── debug.go │ ├── deployment.go │ ├── framework.go │ ├── helm.go │ ├── imagepolicy.go │ ├── job.go │ ├── mutatingadmissionwebhook.go │ ├── namespace.go │ ├── pod.go │ ├── replicasets.go │ ├── replicationcontroller.go │ ├── secret.go │ ├── service.go │ ├── serviceaccount.go │ ├── statefulsets.go │ ├── utils.go │ └── validatingadmissionwebhook.go └── helm │ └── tiller-rbac.yaml └── types └── JSONPatch.go /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | 12 | 13 | 14 | ### What commit ID of Portieris did you experience the problem with? 15 | 16 | ### What went wrong? 17 | 18 | ### What should have happened differently? 19 | 20 | ### How can it be reproduced? 21 | 22 | ### Any other relevant information 23 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Describe the feature request.** 11 | A clear and concise description of what the problem is. For example, `I'm always frustrated when ...` 12 | 13 | **Describe the solution that you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features that you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request. 21 | -------------------------------------------------------------------------------- /.github/workflows/go-portieris.yaml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | test: 11 | name: alltests 12 | runs-on: [ubuntu-latest] 13 | steps: 14 | - name: Check out code into the Go module directory 15 | uses: actions/checkout@v2 16 | with: 17 | path: go/src/github.com/IBM/portieris 18 | 19 | - name: Setup correct Go version 20 | uses: actions/setup-go@v2 21 | with: 22 | go-version: '1.21.11' 23 | check-latest: true 24 | 25 | - name: alltests 26 | run: | 27 | export GOPATH=$GITHUB_WORKSPACE/go 28 | export PATH=$PATH:$GITHUB_WORKSPACE/go/bin/ 29 | export TERM=ansi 30 | cd $GITHUB_WORKSPACE/go/src/github.com/IBM/portieris 31 | make alltests 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | code-generator 2 | cover.* 3 | *.tgz 4 | bin 5 | .idea 6 | helm/portieris/certs/* 7 | scripts/myenv.sh 8 | portieris 9 | deps.jsonl 10 | -------------------------------------------------------------------------------- /.nancy-ignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM/portieris/d89fafec5b73d2043eae451c510de7256b5f7ca0/.nancy-ignore -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # This is an example configuration to enable detect-secrets in the pre-commit hook. 2 | # Add this file to the root folder of your repository. 3 | # 4 | # Read pre-commit hook framework https://pre-commit.com/ for more details about the structure of config yaml file and how git pre-commit would invoke each hook. 5 | # 6 | # This line indicates we will use the hook from ibm/detect-secrets to run scan during committing phase. 7 | # Whitewater/whitewater-detect-secrets would sync code to ibm/detect-secrets upon merge. 8 | repos: 9 | - repo: https://github.com/ibm/detect-secrets 10 | # If you desire to use a specific version of detect-secrets, you can replace `master` with other git revisions such as branch, tag or commit sha. 11 | # You are encouraged to use static refs such as tags, instead of branch name 12 | # 13 | # Running "pre-commit autoupdate" would automatically updates rev to latest tag 14 | rev: 0.13.1+ibm.61.dss 15 | hooks: 16 | - id: detect-secrets # pragma: whitelist secret 17 | # Add options for detect-secrets-hook binary. You can run `detect-secrets-hook --help` to list out all possible options. 18 | # You may also run `pre-commit run detect-secrets` to preview the scan result. 19 | # when "--baseline" without "--use-all-plugins", pre-commit scan with just plugins in baseline file 20 | # when "--baseline" with "--use-all-plugins", pre-commit scan with all available plugins 21 | # add "--fail-on-non-audited" to fail pre-commit for unaudited potential secrets 22 | args: [--baseline, .secrets.baseline, --use-all-plugins ] 23 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | # This first stage of the build uses go-toolset from RHEL. 4 | # We specify a higher version # than available, meaning that the `toolchain` statement in `go.mod` 5 | # will replace go-toolset with the specified version of go. The specified version is then used 6 | # to build the portieris binary. go-toolset is used to create a simplified operating system image 7 | # that satisfies vulnerability scanning requirements 8 | ARG BASE_IMAGE=registry.access.redhat.com/ubi9/go-toolset:1.23.6 9 | FROM $BASE_IMAGE AS builder 10 | ARG PORTIERIS_VERSION=undefined 11 | ARG TARGETOS TARGETARCH 12 | 13 | # prep target rootfs for scratch container 14 | USER root 15 | WORKDIR / 16 | RUN mkdir /image \ 17 | && ln -s usr/bin /image/bin \ 18 | && ln -s usr/sbin /image/sbin \ 19 | && ln -s usr/lib64 /image/lib64 \ 20 | && ln -s usr/lib /image/lib \ 21 | && mkdir -p /image/{usr/bin,usr/lib64,usr/lib,root,home,proc,etc,sys,var,dev} 22 | # see files-{amd64,s390x}.txt for a list of needed files from the UBI image to copy into our 23 | # final "FROM scratch" image; this would need to be modified if any additional 24 | # content was required from UBI for the Portieris binary to function. 25 | COPY files-${TARGETARCH}.txt /tmp 26 | RUN tar cf /tmp/files.tar -T /tmp/files-${TARGETARCH}.txt && tar xf /tmp/files.tar -C /image/ \ 27 | && rpm --root /image --initdb \ 28 | && PACKAGES=$(rpm -qf $(cat /tmp/files-${TARGETARCH}.txt) --queryformat "%{NAME}\n" | grep -v "is not owned by any package" | sort -u) \ 29 | && dnf download --destdir /rpmcache ${PACKAGES} \ 30 | && rpm --root /image -ivh --justdb --nodeps /rpmcache/*.rpm 31 | 32 | # setup workdir and build binary 33 | WORKDIR /go/github.com/IBM/portieris 34 | COPY . ./ 35 | # override GOTOOLCHAIN because go-toolset has a value of local found in /usr/lib/golang/go.env 36 | # this is because we want Go version upgrades to be applied based on the toolchain value in go.mod 37 | # a mounted secret of GOPROXY allows setting of the GOPROXY env var 38 | ENV GOTOOLCHAIN=auto 39 | RUN --mount=type=secret,id=GOPROXY,env=GOPROXY go mod download \ 40 | && CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build \ 41 | -ldflags="-X github.com/IBM/portieris/internal/info.Version=$PORTIERIS_VERSION" -a \ 42 | -tags containers_image_openpgp -o portieris ./cmd/portieris \ 43 | && go version -m -v portieris | (grep dep || true) | awk '{print "{\"Path\": \""$2 "\", \"Version\": \"" $3 "\"}"}' > /deps.jsonl 44 | 45 | # Check dependencies for vulnerabilities 46 | FROM sonatypecommunity/nancy:alpine AS nancy 47 | COPY --from=builder /deps.jsonl / 48 | COPY /.nancy-ignore / 49 | RUN cat /deps.jsonl | nancy --skip-update-check --loud sleuth --no-color 50 | RUN echo true> /nancy-checked 51 | 52 | ################################################################################# 53 | # Finally, copy the minimal image contents and the built binary into the scratch image 54 | FROM scratch 55 | COPY --from=builder /image/ / 56 | COPY --from=builder /go/github.com/IBM/portieris/portieris /portieris 57 | # buildkit skips stages which dont contribute to the final image 58 | COPY --from=nancy /nancy-checked /nancy-checked 59 | # Create /tmp for logs and /run for working directory 60 | RUN [ "/portieris", "--mkdir", "/tmp,/run" ] 61 | WORKDIR /run 62 | # quiet image config checkers, this is the default runAsUser in the deployment 63 | USER 1000060001 64 | CMD ["/portieris","--alsologtostderr","-v=4","2>&1"] 65 | -------------------------------------------------------------------------------- /RELEASE-HOWTO.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | copyright: 4 | years: 2020, 2021 5 | lastupdated: "2021-02-18" 6 | 7 | --- 8 | 9 | # How to create a new release 10 | 11 | Find out about the process that project owners must follow to set expectations and ensure consistency when you are creating a new release. 12 | 13 | ## Version number 14 | 15 | Each release version number must increase incrementally and comply with [Semantic Versioning (SEMVER)](https://semver.org/). 16 | 17 | Use the following format `vx.x.x`. 18 | 19 | ## Determining version number 20 | 21 | Versioning should be performed according to the semver spec. Accordingly, the patch version must be used only for non-breaking bug fixes, and new function should be a minor version increase. 22 | 23 | Since changes to the Helm chart will change behaviour, any change to the Helm chart must be released in a minor version bump. Patch versions of the container image should be consumable without reviewing the Helm chart for changes. 24 | 25 | ## Release process 26 | 27 | 1. Update **VERSION** in the `Makefile`, `Chart.yaml`, and `values.yaml` files to the release version number. 28 | 2. Update the `CHANGELOG.md` file to reference the right version and date. 29 | 3. Update the `go.mod` and `go.sum` files. 30 | 4. Commit the changes. 31 | 5. Run `make alltests`. 32 | 6. Run `make e2e`, or both `make helm.install.local` and `make e2e.quick`. 33 | 7. Publish the image to IBM Cloud Container Registry at `icr.io/portieris` 34 | 8. Create a **tag = VERSION** by running `git tag `. 35 | 9. Create a release that has the chart as a release artifact. 36 | -------------------------------------------------------------------------------- /files-amd64.txt: -------------------------------------------------------------------------------- 1 | etc/pki 2 | usr/lib64/libc.so.6 3 | usr/lib64/libssl.so 4 | usr/lib64/libssl.so.3.2.2 5 | usr/lib64/libdl.so.2 6 | usr/lib64/libpthread.so.0 7 | usr/lib64/libm.so.6 8 | etc/ssl/certs 9 | etc/redhat-release 10 | usr/share/zoneinfo 11 | -------------------------------------------------------------------------------- /files-arm64.txt: -------------------------------------------------------------------------------- 1 | etc/pki 2 | usr/lib64/libc.so.6 3 | usr/lib64/libssl.so 4 | usr/lib64/libssl.so.3.2.2 5 | usr/lib64/libdl.so.2 6 | usr/lib64/libpthread.so.0 7 | usr/lib64/libm.so.6 8 | etc/ssl/certs 9 | etc/redhat-release 10 | usr/share/zoneinfo 11 | -------------------------------------------------------------------------------- /files-s390x.txt: -------------------------------------------------------------------------------- 1 | etc/pki 2 | usr/lib64/libc.so.6 3 | usr/lib64/libssl.so 4 | usr/lib64/libssl.so.3.2.2 5 | usr/lib64/libdl.so.2 6 | usr/lib64/libpthread.so.0 7 | usr/lib64/libm.so.6 8 | etc/ssl/certs 9 | etc/redhat-release 10 | usr/share/zoneinfo 11 | -------------------------------------------------------------------------------- /helm/cleanup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | RELEASE_NAME=${1:-portieris} 4 | NAMESPACE=${2:-portieris} 5 | 6 | echo Deleting release "${RELEASE_NAME}" in "${NAMESPACE}" 7 | 8 | kubectl delete MutatingWebhookConfiguration image-admission-config --ignore-not-found=true 9 | kubectl delete ValidatingWebhookConfiguration image-admission-config --ignore-not-found=true 10 | 11 | kubectl delete crd clusterimagepolicies.securityenforcement.admission.cloud.ibm.com imagepolicies.securityenforcement.admission.cloud.ibm.com --ignore-not-found=true 12 | kubectl delete crd clusterimagepolicies.portieris.cloud.ibm.com imagepolicies.portieris.cloud.ibm.com --ignore-not-found=true 13 | 14 | helm delete "${RELEASE_NAME}" --no-hooks --namespace "${NAMESPACE}" 15 | -------------------------------------------------------------------------------- /helm/portieris/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *~ 18 | # Various IDEs 19 | .project 20 | .idea/ 21 | *.tmproj 22 | -------------------------------------------------------------------------------- /helm/portieris/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | name: portieris 3 | version: v0.13.28 4 | description: Admission Controller webhook for enforcing image trust in your cluster 5 | maintainers: 6 | - name: Stuart Hayton 7 | email: stuart.hayton@uk.ibm.com 8 | - name: Jack Baines 9 | email: jack.baines@uk.ibm.com 10 | - name: Michael Hough 11 | email: michaelh@uk.ibm.com 12 | -------------------------------------------------------------------------------- /helm/portieris/gencerts: -------------------------------------------------------------------------------- 1 | #! /bin/bash -e 2 | 3 | SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 4 | 5 | SERVICE_NAME=portieris 6 | NAMESPACE=${1:-portieris} 7 | 8 | echo "Using $SERVICE_NAME as the service name" 9 | echo "Using $NAMESPACE as the namespace" 10 | 11 | CERT_DIR=$SCRIPT_DIR/certs 12 | 13 | rm -rf "$CERT_DIR" 14 | mkdir -p "$CERT_DIR" 15 | 16 | cat > "$CERT_DIR"/server.conf << EOF 17 | [req] 18 | req_extensions = v3_req 19 | prompt = no 20 | distinguished_name = req_distinguished_name 21 | [req_distinguished_name] 22 | CN = ${SERVICE_NAME}.${NAMESPACE}.svc 23 | [ v3_req ] 24 | basicConstraints = CA:FALSE 25 | keyUsage = nonRepudiation, digitalSignature, keyEncipherment 26 | extendedKeyUsage = clientAuth, serverAuth 27 | subjectAltName = @alt_name 28 | [ alt_name ] 29 | DNS.1 = ${SERVICE_NAME}.${NAMESPACE}.svc 30 | EOF 31 | 32 | # Create a certificate authority 33 | openssl genrsa -out "$CERT_DIR"/caKey.pem 2048 34 | openssl req -x509 -new -nodes -key "$CERT_DIR"/caKey.pem -days 100000 -out "$CERT_DIR"/ca.crt -subj "/CN=${SERVICE_NAME}_ca" 35 | 36 | # Create a server certiticate 37 | openssl genrsa -out "$CERT_DIR"/tls.key 2048 38 | # Note the SAN is the DNS name of the service of the webhook. CN also set but no longer trusted! 39 | openssl req -new -nodes -key "$CERT_DIR"/tls.key -out "$CERT_DIR"/server.csr -config "$CERT_DIR"/server.conf 40 | openssl x509 -req -sha256 -in "$CERT_DIR"/server.csr -CA "$CERT_DIR"/ca.crt -CAkey "$CERT_DIR"/caKey.pem -CAcreateserial -out "$CERT_DIR"/tls.crt -days 100000 -extensions v3_req -extfile "$CERT_DIR"/server.conf 41 | 42 | rm "$CERT_DIR"/caKey.pem "$CERT_DIR"/server.conf "$CERT_DIR"/server.csr 43 | -------------------------------------------------------------------------------- /helm/portieris/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | Portieris is installed in your cluster. 2 | 3 | You should update the policies according to your local requirements, for more information see https://github.com/IBM/portieris/blob/master/POLICIES.md 4 | 5 | -------------------------------------------------------------------------------- /helm/portieris/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=mustache: */}} 2 | {{/* 3 | Expand the name of the chart. 4 | */}} 5 | {{- define "portieris.name" -}} 6 | {{- default .Chart.Name | trunc 63 | trimSuffix "-" -}} 7 | {{- end -}} 8 | 9 | {{/* 10 | Create a default fully qualified app name. 11 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 12 | If release name contains chart name it will be used as a full name. 13 | */}} 14 | {{- define "portieris.fullname" -}} 15 | {{- if .Values.fullnameOverride -}} 16 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} 17 | {{- else -}} 18 | {{- $name := default .Chart.Name .Values.nameOverride -}} 19 | {{- if contains $name .Release.Name -}} 20 | {{- .Release.Name | trunc 63 | trimSuffix "-" -}} 21 | {{- else -}} 22 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} 23 | {{- end -}} 24 | {{- end -}} 25 | {{- end -}} 26 | 27 | {{/* 28 | Create chart name and version as used by the chart label. 29 | */}} 30 | {{- define "portieris.chart" -}} 31 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} 32 | {{- end -}} 33 | -------------------------------------------------------------------------------- /helm/portieris/templates/clusterrole.yaml: -------------------------------------------------------------------------------- 1 | kind: ClusterRole 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | metadata: 4 | name: portieris 5 | labels: 6 | app: {{ template "portieris.name" . }} 7 | chart: {{ template "portieris.chart" . }} 8 | release: {{ .Release.Name }} 9 | heritage: {{ .Release.Service }} 10 | annotations: 11 | {{- if .Capabilities.APIVersions.Has "clusterversions.config.openshift.io" }} 12 | helm.sh/hook: pre-install 13 | helm.sh/hook-weight: "-10" 14 | {{- end }} 15 | rules: 16 | - apiGroups: ["portieris.cloud.ibm.com"] 17 | resources: ["imagepolicies", "clusterimagepolicies"] 18 | verbs: ["get", "watch", "list", "create", "patch"] 19 | - apiGroups: ["apiextensions.k8s.io"] 20 | resources: ["customresourcedefinitions"] 21 | verbs: ["get", "create", "delete"] 22 | - apiGroups: ["admissionregistration.k8s.io"] 23 | resources: ["validatingwebhookconfigurations", "mutatingwebhookconfigurations"] 24 | verbs: ["get", "create", "delete"] 25 | - apiGroups: [""] 26 | resources: ["secrets", "serviceaccounts"] 27 | verbs: ["get"] 28 | -------------------------------------------------------------------------------- /helm/portieris/templates/clusterrolebinding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: admission-portieris-webhook 5 | labels: 6 | app: {{ template "portieris.name" . }} 7 | chart: {{ template "portieris.chart" . }} 8 | release: {{ .Release.Name }} 9 | heritage: {{ .Release.Service }} 10 | annotations: 11 | {{- if .Capabilities.APIVersions.Has "clusterversions.config.openshift.io" }} 12 | helm.sh/hook: pre-install 13 | helm.sh/hook-weight: "-9" 14 | {{- end }} 15 | roleRef: 16 | apiGroup: rbac.authorization.k8s.io 17 | kind: ClusterRole 18 | name: portieris 19 | subjects: 20 | - kind: ServiceAccount 21 | name: portieris 22 | namespace: {{ .Release.Namespace }} 23 | -------------------------------------------------------------------------------- /helm/portieris/templates/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: {{ template "portieris.fullname" . }} 5 | namespace: {{ .Release.Namespace }} 6 | labels: 7 | app: {{ template "portieris.name" . }} 8 | chart: {{ template "portieris.chart" . }} 9 | release: {{ .Release.Name }} 10 | heritage: {{ .Release.Service }} 11 | {{- with .Values.deployemntAnnotations }} 12 | annotations: 13 | {{- toYaml . | nindent 4 }} 14 | {{- end }} 15 | spec: 16 | replicas: {{ .Values.replicaCount }} 17 | selector: 18 | matchLabels: 19 | app: {{ template "portieris.name" . }} 20 | release: {{ .Release.Name }} 21 | template: 22 | metadata: 23 | annotations: 24 | {{- $metric_annotations := dict "prometheus.io/scrape" "true" "prometheus.io/port" "8080" }} 25 | {{- with mergeOverwrite $metric_annotations .Values.podAnnotations }} 26 | {{- toYaml . | nindent 8 }} 27 | {{- end }} 28 | labels: 29 | app: portieris 30 | release: {{ .Release.Name }} 31 | spec: 32 | serviceAccountName: portieris 33 | {{- if .Values.useHostNetwork }} 34 | hostNetwork: true 35 | dnsPolicy: ClusterFirstWithHostNet 36 | {{- end }} 37 | {{- if .Values.priorityClass }} 38 | priorityClassName: {{ .Values.priorityClass }} 39 | {{- end }} 40 | containers: 41 | - name: {{ .Chart.Name }} 42 | image: "{{ .Values.image.host | default "docker.io/ibmcom" }}/{{ .Values.image.image }}:{{ .Values.image.tag }}" 43 | imagePullPolicy: {{ .Values.image.pullPolicy }} 44 | ports: 45 | - name: http 46 | containerPort: 80 47 | protocol: TCP 48 | - name: metrics-port 49 | containerPort: 8080 50 | volumeMounts: 51 | - name: portieris-certs 52 | readOnly: true 53 | mountPath: "/etc/certs" 54 | livenessProbe: 55 | httpGet: 56 | port: 8000 57 | path: "/health/liveness" 58 | scheme: HTTPS 59 | initialDelaySeconds: 10 60 | timeoutSeconds: 10 61 | readinessProbe: 62 | httpGet: 63 | port: 8000 64 | path: "/health/readiness" 65 | scheme: HTTPS 66 | initialDelaySeconds: 10 67 | timeoutSeconds: 10 68 | env: 69 | resources: 70 | {{ toYaml .Values.resources | indent 12 }} 71 | {{- with .Values.nodeSelector }} 72 | nodeSelector: 73 | {{ toYaml . | indent 8 }} 74 | {{- end }} 75 | {{- with .Values.affinity }} 76 | affinity: 77 | {{ toYaml . | indent 8 }} 78 | {{- end }} 79 | {{- with .Values.tolerations }} 80 | tolerations: 81 | {{ toYaml . | indent 8 }} 82 | {{- end }} 83 | securityContext: 84 | runAsUser: {{ .Values.securityContext.runAsUser }} 85 | volumes: 86 | - name: portieris-certs 87 | secret: 88 | secretName: portieris-certs 89 | -------------------------------------------------------------------------------- /helm/portieris/templates/namespace.yaml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM/portieris/d89fafec5b73d2043eae451c510de7256b5f7ca0/helm/portieris/templates/namespace.yaml -------------------------------------------------------------------------------- /helm/portieris/templates/pdb.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.podDisruptionBudget }} 2 | {{- if semverCompare ">=1.21.0-0" .Capabilities.KubeVersion.GitVersion -}} 3 | apiVersion: policy/v1 4 | {{- else -}} 5 | apiVersion: policy/v1beta1 6 | {{- end }} 7 | kind: PodDisruptionBudget 8 | metadata: 9 | name: {{ .Release.Name }} 10 | namespace: {{ .Release.Namespace }} 11 | spec: 12 | {{- if .Values.podDisruptionBudget.minAvailable }} 13 | minAvailable: {{ .Values.podDisruptionBudget.minAvailable }} 14 | {{- end }} 15 | {{- if .Values.podDisruptionBudget.maxUnavailable }} 16 | maxUnavailable: {{ .Values.podDisruptionBudget.maxUnavailable }} 17 | {{- end }} 18 | selector: 19 | matchLabels: 20 | app: {{ .Release.Name }} 21 | {{- end }} 22 | -------------------------------------------------------------------------------- /helm/portieris/templates/policies.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: portieris.cloud.ibm.com/v1 2 | kind: ImagePolicy 3 | metadata: 4 | name: default 5 | namespace: {{ .Release.Namespace }} 6 | annotations: 7 | helm.sh/hook: post-install 8 | helm.sh/hook-weight: "1" 9 | spec: 10 | repositories: 11 | # This policy prevents Portieris from denying its own updates. Do not remove this policy or portieris may not function correctly. 12 | - name: "{{ .Values.image.host | default "docker.io/ibmcom" }}/{{ .Values.image.image }}" 13 | --- 14 | {{- if (eq .Values.PolicySet "IKS") }} 15 | apiVersion: portieris.cloud.ibm.com/v1 16 | kind: ImagePolicy 17 | metadata: 18 | name: default 19 | namespace: kube-system 20 | annotations: 21 | helm.sh/hook: post-install 22 | helm.sh/hook-weight: "1" 23 | spec: 24 | repositories: 25 | # This permissive policy allows all images to be deployed into this namespace. 26 | # IMPORTANT: Review this policy and replace it with one that meets your requirements. 27 | - name: "*" 28 | # These policies allow all IBM Cloud Container Service images to deploy in this namespace. 29 | # IMPORTANT: When you create your own policy in this namespace, be sure to retain these policies. If you do not, the cluster might not update or function properly. 30 | - name: "registry*.bluemix.net/armada/*" 31 | - name: "registry*.bluemix.net/armada-worker/*" 32 | - name: "registry*.bluemix.net/armada-master/*" 33 | - name: "*.icr.io/armada/*" 34 | - name: "*.icr.io/armada-worker/*" 35 | - name: "*.icr.io/armada-master/*" 36 | - name: "icr.io/armada/*" 37 | - name: "icr.io/armada-worker/*" 38 | - name: "icr.io/armada-master/*" 39 | --- 40 | {{ end }} 41 | 42 | {{- if (eq .Values.PolicySet "IKS") }} 43 | apiVersion: portieris.cloud.ibm.com/v1 44 | kind: ImagePolicy 45 | metadata: 46 | name: default 47 | namespace: ibm-system 48 | annotations: 49 | helm.sh/hook: post-install 50 | helm.sh/hook-weight: "1" 51 | spec: 52 | repositories: 53 | # These policies allow all IBM Cloud Container Service images to deploy in this namespace. 54 | # IMPORTANT: When you create your own policy in this namespace, make sure to retain these policies. If you do not, the cluster might not update or function properly. 55 | - name: "registry*.bluemix.net/armada/*" 56 | - name: "registry*.bluemix.net/armada-worker/*" 57 | - name: "registry*.bluemix.net/armada-master/*" 58 | - name: "*.icr.io/armada/*" 59 | - name: "*.icr.io/armada-worker/*" 60 | - name: "*.icr.io/armada-master/*" 61 | - name: "icr.io/armada/*" 62 | - name: "icr.io/armada-worker/*" 63 | - name: "icr.io/armada-master/*" 64 | - name: "icr.io/ext/istio/*" 65 | --- 66 | {{ end }} 67 | 68 | {{- if (eq .Values.PolicySet "IKS") }} 69 | apiVersion: portieris.cloud.ibm.com/v1 70 | kind: ClusterImagePolicy 71 | metadata: 72 | name: default 73 | annotations: 74 | helm.sh/hook: post-install 75 | helm.sh/hook-weight: "1" 76 | spec: 77 | repositories: 78 | {{- .Values.clusterPolicy.allowedRepositories | toYaml | nindent 4 }} 79 | --- 80 | {{ end }} 81 | -------------------------------------------------------------------------------- /helm/portieris/templates/secret.yaml: -------------------------------------------------------------------------------- 1 | {{ if not .Values.SkipSecretCreation }} 2 | {{ if .Values.UseCertManager }} 3 | {{ if not .Values.certManagerIssuer.skipCreation }} 4 | apiVersion: cert-manager.io/v1 5 | kind: {{ .Values.certManagerIssuer.kind }} 6 | metadata: 7 | name: {{ .Values.certManagerIssuer.name }} 8 | namespace: {{ .Release.Namespace }} 9 | spec: 10 | selfSigned: {} 11 | --- 12 | {{ end }} 13 | apiVersion: cert-manager.io/v1 14 | kind: Certificate 15 | metadata: 16 | name: portieris-certs 17 | namespace: {{ .Release.Namespace }} 18 | spec: 19 | dnsNames: 20 | - portieris.{{ .Release.Namespace }}.svc 21 | secretName: portieris-certs 22 | issuerRef: 23 | kind: {{ .Values.certManagerIssuer.kind }} 24 | name: {{ .Values.certManagerIssuer.name }} 25 | {{ else }} 26 | apiVersion: v1 27 | kind: Secret 28 | metadata: 29 | name: portieris-certs 30 | namespace: {{ .Release.Namespace }} 31 | type: Opaque 32 | data: 33 | {{- if .Values.UseGeneratedCerts.enabled }} 34 | tls.crt: {{ required "A valid .Values.UseGeneratedCerts.tlsCert entry required!" .Values.UseGeneratedCerts.tlsCert| b64enc | quote }} 35 | tls.key: {{ required "A valid .Values.UseGeneratedCerts.tlsKey entry required!" .Values.UseGeneratedCerts.tlsKey | b64enc | quote }} 36 | {{ else }} 37 | tls.crt: {{ .Files.Get "certs/tls.crt" | b64enc }} 38 | tls.key: {{ .Files.Get "certs/tls.key" | b64enc }} 39 | {{- end }} 40 | {{ end }} 41 | {{ end }} 42 | -------------------------------------------------------------------------------- /helm/portieris/templates/securitycontextconstraint.yaml: -------------------------------------------------------------------------------- 1 | {{ if .Capabilities.APIVersions.Has "config.openshift.io/v1/ClusterVersion" }} 2 | apiVersion: security.openshift.io/v1 3 | kind: SecurityContextConstraints 4 | metadata: 5 | annotations: 6 | kubernetes.io/description: anyuid provides all features of the restricted SCC but allows users to run with any UID and any GID. 7 | helm.sh/hook: pre-install 8 | helm.sh/hook-weight: "1" 9 | name: anyuid-portieris 10 | allowHostDirVolumePlugin: false 11 | allowHostIPC: false 12 | allowHostNetwork: false 13 | allowHostPID: false 14 | allowHostPorts: false 15 | allowPrivilegeEscalation: true 16 | allowPrivilegedContainer: false 17 | allowedCapabilities: [] 18 | defaultAddCapabilities: [] 19 | fsGroup: 20 | type: RunAsAny 21 | priority: 10 22 | readOnlyRootFilesystem: false 23 | requiredDropCapabilities: 24 | - MKNOD 25 | runAsUser: 26 | type: MustRunAs 27 | uid: {{ .Values.securityContext.runAsUser }} 28 | seLinuxContext: 29 | type: MustRunAs 30 | supplementalGroups: 31 | type: RunAsAny 32 | users: 33 | - system:serviceaccount:portieris:portieris 34 | volumes: 35 | - configMap 36 | - downwardAPI 37 | - emptyDir 38 | - persistentVolumeClaim 39 | - projected 40 | - secret 41 | {{ end }} 42 | -------------------------------------------------------------------------------- /helm/portieris/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ template "portieris.name" . }} 5 | namespace: {{ .Release.Namespace }} 6 | labels: 7 | app: {{ template "portieris.name" . }} 8 | chart: {{ template "portieris.chart" . }} 9 | release: {{ .Release.Name }} 10 | heritage: {{ .Release.Service }} 11 | spec: 12 | type: {{ .Values.service.type }} 13 | ports: 14 | - port: {{ .Values.service.port }} 15 | targetPort: {{ .Values.service.targetPort }} 16 | protocol: TCP 17 | name: https 18 | - port: {{ .Values.service.metricsPort }} 19 | targetPort: {{ .Values.service.metricsPort }} 20 | protocol: TCP 21 | name: metrics 22 | selector: 23 | app: {{ template "portieris.name" . }} 24 | {{- if .Values.selectByRelease }} 25 | release: {{ .Release.Name }} 26 | {{ end }} -------------------------------------------------------------------------------- /helm/portieris/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | {{ if .Values.image.pullSecret }} 4 | imagePullSecrets: 5 | - name: {{ .Values.image.pullSecret }} 6 | {{ end }} 7 | metadata: 8 | name: portieris 9 | namespace: {{ .Release.Namespace }} 10 | labels: 11 | app: {{ template "portieris.name" . }} 12 | chart: {{ template "portieris.chart" . }} 13 | release: {{ .Release.Name }} 14 | heritage: {{ .Release.Service }} 15 | annotations: 16 | {{- if .Capabilities.APIVersions.Has "clusterversions.config.openshift.io" }} 17 | helm.sh/hook: pre-install 18 | helm.sh/hook-weight: "-7" 19 | {{- end }} 20 | -------------------------------------------------------------------------------- /helpers/credential/credential.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package credential 16 | 17 | // Credentials is a slice of username/password combinations 18 | type Credentials []Credential 19 | 20 | // Credential will hold a username/password combination from a pull secret in the pod being considered for admission 21 | type Credential struct { 22 | Username string 23 | Password string 24 | } 25 | -------------------------------------------------------------------------------- /helpers/oauth/token.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package oauth 16 | 17 | import ( 18 | "time" 19 | ) 20 | 21 | // TokenResponse is a generic OAuth2 token response 22 | type TokenResponse struct { 23 | Token string `json:"token,omitempty"` 24 | AccessToken string `json:"access_token,omitempty"` 25 | RefreshToken string `json:"refresh_token,omitempty"` 26 | ExpiresIn int `json:"expires_in,omitempty"` 27 | IssuedAt time.Time `json:"issued_at,omitempty"` 28 | } 29 | -------------------------------------------------------------------------------- /helpers/trustmap/trust_server_map.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018, 2022 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package trustmap 16 | 17 | // TrustServerFn A simple type alias to represent a function that takes image and suffix and returns trust url 18 | type TrustServerFn func(string, string) string 19 | 20 | // Identity Returns a configured function that just returns a string. For static trust server hosts. 21 | func Identity(value string) TrustServerFn { 22 | return func(registryHostname string, imageHostname string) string { 23 | return value 24 | } 25 | } 26 | 27 | // TrustServerMap Easy way to link known registries to their sponsored trust servers 28 | var TrustServerMap = map[string]TrustServerFn{ 29 | "docker.io": Identity("https://notary.docker.io"), 30 | "quay.io": Identity("https://quay.io:443"), 31 | } 32 | -------------------------------------------------------------------------------- /helpers/useragent/useragent.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package useragent 16 | 17 | import ( 18 | "net/http" 19 | 20 | "github.com/IBM/portieris/internal/info" 21 | ) 22 | 23 | // Set is a http.RoundTripper which adds the User-Agent header to all requests. 24 | type Set struct { 25 | Transport http.RoundTripper 26 | } 27 | 28 | // RoundTrip sets the User-Agent on the request and then calls the underlying 29 | // Transport. 30 | func (a *Set) RoundTrip(r *http.Request) (*http.Response, error) { 31 | r.Header.Set("User-Agent", "portieris/"+info.Version) 32 | return a.Transport.RoundTrip(r) 33 | } 34 | -------------------------------------------------------------------------------- /helpers/useragent/useragent_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package useragent 16 | 17 | import ( 18 | "net/http" 19 | "net/http/httptest" 20 | "testing" 21 | 22 | "github.com/stretchr/testify/assert" 23 | "github.com/stretchr/testify/require" 24 | ) 25 | 26 | func TestSet_RoundTrip(t *testing.T) { 27 | tests := map[string]struct { 28 | wantStatus int 29 | wantErr bool 30 | }{ 31 | "good path": { 32 | wantStatus: http.StatusTeapot, 33 | }, 34 | "error path": { 35 | wantErr: true, 36 | }, 37 | } 38 | for name, test := range tests { 39 | t.Run(name, func(t *testing.T) { 40 | c := &http.Client{ 41 | Transport: &Set{ 42 | Transport: http.DefaultTransport, 43 | }, 44 | } 45 | 46 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 47 | assert.Equal(t, r.Header.Get("User-Agent"), "portieris/undefined") 48 | w.WriteHeader(test.wantStatus) 49 | })) 50 | defer ts.Close() 51 | 52 | r, err := http.NewRequest(http.MethodGet, ts.URL, nil) 53 | require.NoError(t, err) 54 | if test.wantErr { 55 | r, err = http.NewRequest(http.MethodGet, "htootyps://notaurl", nil) 56 | require.NoError(t, err) 57 | } 58 | 59 | res, err := c.Do(r) 60 | 61 | if (err != nil) != test.wantErr { 62 | t.Errorf("error = %v, wantErr %v", err, test.wantErr) 63 | return 64 | } 65 | if !test.wantErr { 66 | assert.Equal(t, res.StatusCode, test.wantStatus) 67 | } 68 | }) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /helpers/wildcard/wildcard.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package wildcard 16 | 17 | import ( 18 | "strings" 19 | ) 20 | 21 | // Wildcard character 22 | const wildcard = "*" 23 | 24 | // Compare will match a string pattern which may contain wildcard 25 | // characters against a string str. The result is a boolean. 26 | func Compare(pattern, str string) bool { 27 | // Empty pattern can only match empty str 28 | if pattern == "" { 29 | return str == pattern 30 | } 31 | 32 | // If the pattern is a single wildcard character, it matches everything 33 | if pattern == wildcard { 34 | return true 35 | } 36 | 37 | // Break the pattern into pieces by wildcard characters 38 | pieces := strings.Split(pattern, wildcard) 39 | 40 | // If there is a single piece, there were no wildcard characters, 41 | // so test for equal strings. 42 | if len(pieces) == 1 { 43 | return str == pattern 44 | } 45 | 46 | // Does pattern start or end in a wildcard? 47 | startingWildcard := strings.HasPrefix(pattern, wildcard) 48 | endingWildcard := strings.HasSuffix(pattern, wildcard) 49 | 50 | // Don't test trailing piece as it requires different logic 51 | numPieces := len(pieces) - 1 52 | 53 | // Go over the pieces and ensure they match. 54 | for i := 0; i < numPieces; i++ { 55 | index := strings.Index(str, pieces[i]) 56 | 57 | switch i { 58 | // First piece has different logic 59 | case 0: 60 | if !startingWildcard && index != 0 { 61 | return false 62 | } 63 | default: 64 | // Ensure that the pieces match 65 | if index < 0 { 66 | return false 67 | } 68 | } 69 | 70 | // Trim piece from str 71 | str = str[index+len(pieces[i]):] 72 | } 73 | 74 | // Reached the last piece. 75 | return endingWildcard || strings.HasSuffix(str, pieces[numPieces]) 76 | } 77 | 78 | // CompareAnyTag will match a string pattern which may contain wildcard 79 | // characters against a string str. If the compare fails, a successive compare 80 | // is made with a ':*' added to pattern (wildcard the addition of a tag to the pattern). 81 | // The result is a boolean based on the last test. 82 | func CompareAnyTag(pattern, str string) bool { 83 | if !Compare(pattern, str) { 84 | return Compare(pattern+":*", str) 85 | } 86 | return true 87 | } 88 | -------------------------------------------------------------------------------- /internal/info/info.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package info 16 | 17 | // Version is replaced with the correct value when portieris is built using the 18 | // Makefile. 19 | var Version = "undefined" 20 | -------------------------------------------------------------------------------- /logos/text_only.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | Portieris_Logo 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /pkg/apis/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | copyright: 4 | years: 2021 5 | lastupdated: "2021-05-12" 6 | 7 | --- 8 | 9 | # Image Policy CRD (CustomResourceDefinition) 10 | 11 | ## Regenerate the CRD files with code-generator 12 | 13 | **Important:** You only have to regenerate the customer resource definition (CRD) files if you're a developer and you're changing the design of the CRD policies. 14 | 15 | Some scripts from the [k8s.io/code-generator](https://github.com/kubernetes/code-generator) repository are used to generate the clientset, informers, listers, and deep-copy functions. 16 | 17 | ### Prerequisites 18 | 19 | Clone the code-generator repository that is compatible with type of the API that you're using. 20 | 21 | ```bash 22 | make code-generator 23 | ``` 24 | 25 | #### Generate the CRDs 26 | 27 | ```bash 28 | make regenerate 29 | ``` 30 | -------------------------------------------------------------------------------- /pkg/apis/portieris.cloud.ibm.com/client/clientset/versioned/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | // This package has the automatically generated clientset. 20 | package versioned 21 | -------------------------------------------------------------------------------- /pkg/apis/portieris.cloud.ibm.com/client/clientset/versioned/fake/clientset_generated.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | package fake 20 | 21 | import ( 22 | clientset "github.com/IBM/portieris/pkg/apis/portieris.cloud.ibm.com/client/clientset/versioned" 23 | portierisv1 "github.com/IBM/portieris/pkg/apis/portieris.cloud.ibm.com/client/clientset/versioned/typed/portieris.cloud.ibm.com/v1" 24 | fakeportierisv1 "github.com/IBM/portieris/pkg/apis/portieris.cloud.ibm.com/client/clientset/versioned/typed/portieris.cloud.ibm.com/v1/fake" 25 | "k8s.io/apimachinery/pkg/runtime" 26 | "k8s.io/apimachinery/pkg/watch" 27 | "k8s.io/client-go/discovery" 28 | fakediscovery "k8s.io/client-go/discovery/fake" 29 | "k8s.io/client-go/testing" 30 | ) 31 | 32 | // NewSimpleClientset returns a clientset that will respond with the provided objects. 33 | // It's backed by a very simple object tracker that processes creates, updates and deletions as-is, 34 | // without applying any validations and/or defaults. It shouldn't be considered a replacement 35 | // for a real clientset and is mostly useful in simple unit tests. 36 | func NewSimpleClientset(objects ...runtime.Object) *Clientset { 37 | o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder()) 38 | for _, obj := range objects { 39 | if err := o.Add(obj); err != nil { 40 | panic(err) 41 | } 42 | } 43 | 44 | cs := &Clientset{tracker: o} 45 | cs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake} 46 | cs.AddReactor("*", "*", testing.ObjectReaction(o)) 47 | cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) { 48 | gvr := action.GetResource() 49 | ns := action.GetNamespace() 50 | watch, err := o.Watch(gvr, ns) 51 | if err != nil { 52 | return false, nil, err 53 | } 54 | return true, watch, nil 55 | }) 56 | 57 | return cs 58 | } 59 | 60 | // Clientset implements clientset.Interface. Meant to be embedded into a 61 | // struct to get a default implementation. This makes faking out just the method 62 | // you want to test easier. 63 | type Clientset struct { 64 | testing.Fake 65 | discovery *fakediscovery.FakeDiscovery 66 | tracker testing.ObjectTracker 67 | } 68 | 69 | func (c *Clientset) Discovery() discovery.DiscoveryInterface { 70 | return c.discovery 71 | } 72 | 73 | func (c *Clientset) Tracker() testing.ObjectTracker { 74 | return c.tracker 75 | } 76 | 77 | var ( 78 | _ clientset.Interface = &Clientset{} 79 | _ testing.FakeClient = &Clientset{} 80 | ) 81 | 82 | // PortierisV1 retrieves the PortierisV1Client 83 | func (c *Clientset) PortierisV1() portierisv1.PortierisV1Interface { 84 | return &fakeportierisv1.FakePortierisV1{Fake: &c.Fake} 85 | } 86 | -------------------------------------------------------------------------------- /pkg/apis/portieris.cloud.ibm.com/client/clientset/versioned/fake/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | // This package has the automatically generated fake clientset. 20 | package fake 21 | -------------------------------------------------------------------------------- /pkg/apis/portieris.cloud.ibm.com/client/clientset/versioned/fake/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | package fake 20 | 21 | import ( 22 | portierisv1 "github.com/IBM/portieris/pkg/apis/portieris.cloud.ibm.com/v1" 23 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 24 | runtime "k8s.io/apimachinery/pkg/runtime" 25 | schema "k8s.io/apimachinery/pkg/runtime/schema" 26 | serializer "k8s.io/apimachinery/pkg/runtime/serializer" 27 | utilruntime "k8s.io/apimachinery/pkg/util/runtime" 28 | ) 29 | 30 | var scheme = runtime.NewScheme() 31 | var codecs = serializer.NewCodecFactory(scheme) 32 | 33 | var localSchemeBuilder = runtime.SchemeBuilder{ 34 | portierisv1.AddToScheme, 35 | } 36 | 37 | // AddToScheme adds all types of this clientset into the given scheme. This allows composition 38 | // of clientsets, like in: 39 | // 40 | // import ( 41 | // "k8s.io/client-go/kubernetes" 42 | // clientsetscheme "k8s.io/client-go/kubernetes/scheme" 43 | // aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" 44 | // ) 45 | // 46 | // kclientset, _ := kubernetes.NewForConfig(c) 47 | // _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) 48 | // 49 | // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types 50 | // correctly. 51 | var AddToScheme = localSchemeBuilder.AddToScheme 52 | 53 | func init() { 54 | v1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"}) 55 | utilruntime.Must(AddToScheme(scheme)) 56 | } 57 | -------------------------------------------------------------------------------- /pkg/apis/portieris.cloud.ibm.com/client/clientset/versioned/scheme/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | // This package contains the scheme of the automatically generated clientset. 20 | package scheme 21 | -------------------------------------------------------------------------------- /pkg/apis/portieris.cloud.ibm.com/client/clientset/versioned/scheme/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | package scheme 20 | 21 | import ( 22 | portierisv1 "github.com/IBM/portieris/pkg/apis/portieris.cloud.ibm.com/v1" 23 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 24 | runtime "k8s.io/apimachinery/pkg/runtime" 25 | schema "k8s.io/apimachinery/pkg/runtime/schema" 26 | serializer "k8s.io/apimachinery/pkg/runtime/serializer" 27 | utilruntime "k8s.io/apimachinery/pkg/util/runtime" 28 | ) 29 | 30 | var Scheme = runtime.NewScheme() 31 | var Codecs = serializer.NewCodecFactory(Scheme) 32 | var ParameterCodec = runtime.NewParameterCodec(Scheme) 33 | var localSchemeBuilder = runtime.SchemeBuilder{ 34 | portierisv1.AddToScheme, 35 | } 36 | 37 | // AddToScheme adds all types of this clientset into the given scheme. This allows composition 38 | // of clientsets, like in: 39 | // 40 | // import ( 41 | // "k8s.io/client-go/kubernetes" 42 | // clientsetscheme "k8s.io/client-go/kubernetes/scheme" 43 | // aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" 44 | // ) 45 | // 46 | // kclientset, _ := kubernetes.NewForConfig(c) 47 | // _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) 48 | // 49 | // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types 50 | // correctly. 51 | var AddToScheme = localSchemeBuilder.AddToScheme 52 | 53 | func init() { 54 | v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"}) 55 | utilruntime.Must(AddToScheme(Scheme)) 56 | } 57 | -------------------------------------------------------------------------------- /pkg/apis/portieris.cloud.ibm.com/client/clientset/versioned/typed/portieris.cloud.ibm.com/v1/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | // This package has the automatically generated typed clients. 20 | package v1 21 | -------------------------------------------------------------------------------- /pkg/apis/portieris.cloud.ibm.com/client/clientset/versioned/typed/portieris.cloud.ibm.com/v1/fake/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | // Package fake has the automatically generated clients. 20 | package fake 21 | -------------------------------------------------------------------------------- /pkg/apis/portieris.cloud.ibm.com/client/clientset/versioned/typed/portieris.cloud.ibm.com/v1/fake/fake_portieris.cloud.ibm.com_client.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | package fake 20 | 21 | import ( 22 | v1 "github.com/IBM/portieris/pkg/apis/portieris.cloud.ibm.com/client/clientset/versioned/typed/portieris.cloud.ibm.com/v1" 23 | rest "k8s.io/client-go/rest" 24 | testing "k8s.io/client-go/testing" 25 | ) 26 | 27 | type FakePortierisV1 struct { 28 | *testing.Fake 29 | } 30 | 31 | func (c *FakePortierisV1) ClusterImagePolicies() v1.ClusterImagePolicyInterface { 32 | return &FakeClusterImagePolicies{c} 33 | } 34 | 35 | func (c *FakePortierisV1) ImagePolicies(namespace string) v1.ImagePolicyInterface { 36 | return &FakeImagePolicies{c, namespace} 37 | } 38 | 39 | // RESTClient returns a RESTClient that is used to communicate 40 | // with API server by this client implementation. 41 | func (c *FakePortierisV1) RESTClient() rest.Interface { 42 | var ret *rest.RESTClient 43 | return ret 44 | } 45 | -------------------------------------------------------------------------------- /pkg/apis/portieris.cloud.ibm.com/client/clientset/versioned/typed/portieris.cloud.ibm.com/v1/generated_expansion.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | package v1 20 | 21 | type ClusterImagePolicyExpansion interface{} 22 | 23 | type ImagePolicyExpansion interface{} 24 | -------------------------------------------------------------------------------- /pkg/apis/portieris.cloud.ibm.com/client/informers/externalversions/generic.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by informer-gen. DO NOT EDIT. 18 | 19 | package externalversions 20 | 21 | import ( 22 | "fmt" 23 | 24 | v1 "github.com/IBM/portieris/pkg/apis/portieris.cloud.ibm.com/v1" 25 | schema "k8s.io/apimachinery/pkg/runtime/schema" 26 | cache "k8s.io/client-go/tools/cache" 27 | ) 28 | 29 | // GenericInformer is type of SharedIndexInformer which will locate and delegate to other 30 | // sharedInformers based on type 31 | type GenericInformer interface { 32 | Informer() cache.SharedIndexInformer 33 | Lister() cache.GenericLister 34 | } 35 | 36 | type genericInformer struct { 37 | informer cache.SharedIndexInformer 38 | resource schema.GroupResource 39 | } 40 | 41 | // Informer returns the SharedIndexInformer. 42 | func (f *genericInformer) Informer() cache.SharedIndexInformer { 43 | return f.informer 44 | } 45 | 46 | // Lister returns the GenericLister. 47 | func (f *genericInformer) Lister() cache.GenericLister { 48 | return cache.NewGenericLister(f.Informer().GetIndexer(), f.resource) 49 | } 50 | 51 | // ForResource gives generic access to a shared informer of the matching type 52 | // TODO extend this to unknown resources with a client pool 53 | func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) { 54 | switch resource { 55 | // Group=portieris.cloud.ibm.com, Version=v1 56 | case v1.SchemeGroupVersion.WithResource("clusterimagepolicies"): 57 | return &genericInformer{resource: resource.GroupResource(), informer: f.Portieris().V1().ClusterImagePolicies().Informer()}, nil 58 | case v1.SchemeGroupVersion.WithResource("imagepolicies"): 59 | return &genericInformer{resource: resource.GroupResource(), informer: f.Portieris().V1().ImagePolicies().Informer()}, nil 60 | 61 | } 62 | 63 | return nil, fmt.Errorf("no informer found for %v", resource) 64 | } 65 | -------------------------------------------------------------------------------- /pkg/apis/portieris.cloud.ibm.com/client/informers/externalversions/internalinterfaces/factory_interfaces.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by informer-gen. DO NOT EDIT. 18 | 19 | package internalinterfaces 20 | 21 | import ( 22 | time "time" 23 | 24 | versioned "github.com/IBM/portieris/pkg/apis/portieris.cloud.ibm.com/client/clientset/versioned" 25 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 | runtime "k8s.io/apimachinery/pkg/runtime" 27 | cache "k8s.io/client-go/tools/cache" 28 | ) 29 | 30 | // NewInformerFunc takes versioned.Interface and time.Duration to return a SharedIndexInformer. 31 | type NewInformerFunc func(versioned.Interface, time.Duration) cache.SharedIndexInformer 32 | 33 | // SharedInformerFactory a small interface to allow for adding an informer without an import cycle 34 | type SharedInformerFactory interface { 35 | Start(stopCh <-chan struct{}) 36 | InformerFor(obj runtime.Object, newFunc NewInformerFunc) cache.SharedIndexInformer 37 | } 38 | 39 | // TweakListOptionsFunc is a function that transforms a v1.ListOptions. 40 | type TweakListOptionsFunc func(*v1.ListOptions) 41 | -------------------------------------------------------------------------------- /pkg/apis/portieris.cloud.ibm.com/client/informers/externalversions/portieris.cloud.ibm.com/interface.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by informer-gen. DO NOT EDIT. 18 | 19 | package portieris 20 | 21 | import ( 22 | internalinterfaces "github.com/IBM/portieris/pkg/apis/portieris.cloud.ibm.com/client/informers/externalversions/internalinterfaces" 23 | v1 "github.com/IBM/portieris/pkg/apis/portieris.cloud.ibm.com/client/informers/externalversions/portieris.cloud.ibm.com/v1" 24 | ) 25 | 26 | // Interface provides access to each of this group's versions. 27 | type Interface interface { 28 | // V1 provides access to shared informers for resources in V1. 29 | V1() v1.Interface 30 | } 31 | 32 | type group struct { 33 | factory internalinterfaces.SharedInformerFactory 34 | namespace string 35 | tweakListOptions internalinterfaces.TweakListOptionsFunc 36 | } 37 | 38 | // New returns a new Interface. 39 | func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { 40 | return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} 41 | } 42 | 43 | // V1 returns a new v1.Interface. 44 | func (g *group) V1() v1.Interface { 45 | return v1.New(g.factory, g.namespace, g.tweakListOptions) 46 | } 47 | -------------------------------------------------------------------------------- /pkg/apis/portieris.cloud.ibm.com/client/informers/externalversions/portieris.cloud.ibm.com/v1/interface.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by informer-gen. DO NOT EDIT. 18 | 19 | package v1 20 | 21 | import ( 22 | internalinterfaces "github.com/IBM/portieris/pkg/apis/portieris.cloud.ibm.com/client/informers/externalversions/internalinterfaces" 23 | ) 24 | 25 | // Interface provides access to all the informers in this group version. 26 | type Interface interface { 27 | // ClusterImagePolicies returns a ClusterImagePolicyInformer. 28 | ClusterImagePolicies() ClusterImagePolicyInformer 29 | // ImagePolicies returns a ImagePolicyInformer. 30 | ImagePolicies() ImagePolicyInformer 31 | } 32 | 33 | type version struct { 34 | factory internalinterfaces.SharedInformerFactory 35 | namespace string 36 | tweakListOptions internalinterfaces.TweakListOptionsFunc 37 | } 38 | 39 | // New returns a new Interface. 40 | func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { 41 | return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} 42 | } 43 | 44 | // ClusterImagePolicies returns a ClusterImagePolicyInformer. 45 | func (v *version) ClusterImagePolicies() ClusterImagePolicyInformer { 46 | return &clusterImagePolicyInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} 47 | } 48 | 49 | // ImagePolicies returns a ImagePolicyInformer. 50 | func (v *version) ImagePolicies() ImagePolicyInformer { 51 | return &imagePolicyInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} 52 | } 53 | -------------------------------------------------------------------------------- /pkg/apis/portieris.cloud.ibm.com/client/listers/portieris.cloud.ibm.com/v1/clusterimagepolicy.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by lister-gen. DO NOT EDIT. 18 | 19 | package v1 20 | 21 | import ( 22 | v1 "github.com/IBM/portieris/pkg/apis/portieris.cloud.ibm.com/v1" 23 | "k8s.io/apimachinery/pkg/api/errors" 24 | "k8s.io/apimachinery/pkg/labels" 25 | "k8s.io/client-go/tools/cache" 26 | ) 27 | 28 | // ClusterImagePolicyLister helps list ClusterImagePolicies. 29 | // All objects returned here must be treated as read-only. 30 | type ClusterImagePolicyLister interface { 31 | // List lists all ClusterImagePolicies in the indexer. 32 | // Objects returned here must be treated as read-only. 33 | List(selector labels.Selector) (ret []*v1.ClusterImagePolicy, err error) 34 | // Get retrieves the ClusterImagePolicy from the index for a given name. 35 | // Objects returned here must be treated as read-only. 36 | Get(name string) (*v1.ClusterImagePolicy, error) 37 | ClusterImagePolicyListerExpansion 38 | } 39 | 40 | // clusterImagePolicyLister implements the ClusterImagePolicyLister interface. 41 | type clusterImagePolicyLister struct { 42 | indexer cache.Indexer 43 | } 44 | 45 | // NewClusterImagePolicyLister returns a new ClusterImagePolicyLister. 46 | func NewClusterImagePolicyLister(indexer cache.Indexer) ClusterImagePolicyLister { 47 | return &clusterImagePolicyLister{indexer: indexer} 48 | } 49 | 50 | // List lists all ClusterImagePolicies in the indexer. 51 | func (s *clusterImagePolicyLister) List(selector labels.Selector) (ret []*v1.ClusterImagePolicy, err error) { 52 | err = cache.ListAll(s.indexer, selector, func(m interface{}) { 53 | ret = append(ret, m.(*v1.ClusterImagePolicy)) 54 | }) 55 | return ret, err 56 | } 57 | 58 | // Get retrieves the ClusterImagePolicy from the index for a given name. 59 | func (s *clusterImagePolicyLister) Get(name string) (*v1.ClusterImagePolicy, error) { 60 | obj, exists, err := s.indexer.GetByKey(name) 61 | if err != nil { 62 | return nil, err 63 | } 64 | if !exists { 65 | return nil, errors.NewNotFound(v1.Resource("clusterimagepolicy"), name) 66 | } 67 | return obj.(*v1.ClusterImagePolicy), nil 68 | } 69 | -------------------------------------------------------------------------------- /pkg/apis/portieris.cloud.ibm.com/client/listers/portieris.cloud.ibm.com/v1/expansion_generated.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by lister-gen. DO NOT EDIT. 18 | 19 | package v1 20 | 21 | // ClusterImagePolicyListerExpansion allows custom methods to be added to 22 | // ClusterImagePolicyLister. 23 | type ClusterImagePolicyListerExpansion interface{} 24 | 25 | // ImagePolicyListerExpansion allows custom methods to be added to 26 | // ImagePolicyLister. 27 | type ImagePolicyListerExpansion interface{} 28 | 29 | // ImagePolicyNamespaceListerExpansion allows custom methods to be added to 30 | // ImagePolicyNamespaceLister. 31 | type ImagePolicyNamespaceListerExpansion interface{} 32 | -------------------------------------------------------------------------------- /pkg/apis/portieris.cloud.ibm.com/register.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package portieriscloudibmcom 16 | 17 | const ( 18 | //GroupName . 19 | GroupName = "portieris.cloud.ibm.com" 20 | ) 21 | -------------------------------------------------------------------------------- /pkg/apis/portieris.cloud.ibm.com/v1/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // +k8s:deepcopy-gen=package 16 | 17 | // Package v1 is the v1 version of the API. 18 | // +groupName=portieris.cloud.ibm.com 19 | package v1 20 | -------------------------------------------------------------------------------- /pkg/apis/portieris.cloud.ibm.com/v1/register.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package v1 16 | 17 | import ( 18 | policy "github.com/IBM/portieris/pkg/apis/portieris.cloud.ibm.com" 19 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 20 | "k8s.io/apimachinery/pkg/runtime" 21 | "k8s.io/apimachinery/pkg/runtime/schema" 22 | ) 23 | 24 | // SchemeGroupVersion is group version used to register these objects 25 | var SchemeGroupVersion = schema.GroupVersion{Group: policy.GroupName, Version: "v1"} 26 | 27 | // Kind takes an unqualified kind and returns back a Group qualified GroupKind 28 | func Kind(kind string) schema.GroupKind { 29 | return SchemeGroupVersion.WithKind(kind).GroupKind() 30 | } 31 | 32 | // Resource takes an unqualified resource and returns a Group qualified GroupResource 33 | func Resource(resource string) schema.GroupResource { 34 | return SchemeGroupVersion.WithResource(resource).GroupResource() 35 | } 36 | 37 | var ( 38 | // SchemeBuilder . 39 | SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) 40 | // AddToScheme . 41 | AddToScheme = SchemeBuilder.AddToScheme 42 | ) 43 | 44 | // Adds the list of known types to Scheme. 45 | func addKnownTypes(scheme *runtime.Scheme) error { 46 | scheme.AddKnownTypes(SchemeGroupVersion, 47 | &ImagePolicy{}, 48 | &ImagePolicyList{}, 49 | &ClusterImagePolicy{}, 50 | &ClusterImagePolicyList{}, 51 | ) 52 | metav1.AddToGroupVersion(scheme, SchemeGroupVersion) 53 | return nil 54 | } 55 | -------------------------------------------------------------------------------- /pkg/controller/fakecontroller/controller.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018,2021 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package fakecontroller 16 | 17 | import admissionv1 "k8s.io/api/admission/v1" 18 | 19 | // Controller is a fake controller for stubbing 20 | type Controller struct { 21 | } 22 | 23 | // Admit is a fake admit function for stubbing 24 | func (c *Controller) Admit(admissionRequest *admissionv1.AdmissionRequest) *admissionv1.AdmissionResponse { 25 | return &admissionv1.AdmissionResponse{Allowed: true} 26 | } 27 | -------------------------------------------------------------------------------- /pkg/controller/interface.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018,2021 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package controller 16 | 17 | import admissionv1 "k8s.io/api/admission/v1" 18 | 19 | // Interface are the methods required to implement a controller for the webhook package 20 | type Interface interface { 21 | Admit(*admissionv1.AdmissionRequest) *admissionv1.AdmissionResponse 22 | } 23 | -------------------------------------------------------------------------------- /pkg/kubernetes/wrapper.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018, 2021 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package kubernetes 16 | 17 | import ( 18 | admissionv1 "k8s.io/api/admission/v1" 19 | corev1 "k8s.io/api/core/v1" 20 | "k8s.io/apimachinery/pkg/runtime" 21 | "k8s.io/apimachinery/pkg/runtime/serializer" 22 | "k8s.io/client-go/kubernetes" 23 | ) 24 | 25 | var codec = serializer.NewCodecFactory(runtime.NewScheme()) 26 | 27 | var _ WrapperInterface = &Wrapper{} 28 | 29 | // WrapperInterface is the interface for a wrapper around kubeclientset that includes some helper functions for applying behaviour to kube resources 30 | type WrapperInterface interface { 31 | kubernetes.Interface 32 | GetPodSpec(*admissionv1.AdmissionRequest) (string, *corev1.PodSpec, error) 33 | GetSecretToken(namespace, secretName, registry string) (string, string, error) 34 | GetSecretKey(namespace, secretName string) ([]byte, error) 35 | GetBasicCredentials(namespace, secretName string) (string, string, error) 36 | } 37 | 38 | // Wrapper is a wrapper around kubeclientset that includes some helper functions for applying behaviour to kube resources 39 | type Wrapper struct { 40 | kubernetes.Interface 41 | } 42 | 43 | // NewKubeClientsetWrapper creates a wrapper from the kubeclientset passed in 44 | func NewKubeClientsetWrapper(kubeClientset kubernetes.Interface) *Wrapper { 45 | return &Wrapper{kubeClientset} 46 | } 47 | -------------------------------------------------------------------------------- /pkg/metrics/metrics.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package metrics 16 | 17 | import ( 18 | "fmt" 19 | "net/http" 20 | 21 | "github.com/prometheus/client_golang/prometheus" 22 | "github.com/prometheus/client_golang/prometheus/promhttp" 23 | ) 24 | 25 | // PortierisMetrics implements the metrics for Portieris 26 | type PortierisMetrics struct { 27 | AllowDecisionCount prometheus.Counter 28 | DenyDecisionCount prometheus.Counter 29 | 30 | allMetrics []prometheus.Collector 31 | } 32 | 33 | // NewMetrics instantiates PortierisMetrics 34 | func NewMetrics() *PortierisMetrics { 35 | p := &PortierisMetrics{} 36 | p.AllowDecisionCount = p.counter("allow_count", "Allow") 37 | p.DenyDecisionCount = p.counter("deny_count", "Deny") 38 | prometheus.MustRegister(p.allMetrics...) 39 | return p 40 | } 41 | 42 | func (p *PortierisMetrics) counter(name, help string) prometheus.Counter { 43 | result := prometheus.NewCounter(prometheus.CounterOpts{ 44 | Name: metricName(name), 45 | Help: metricHelp(help), 46 | }) 47 | 48 | p.allMetrics = append(p.allMetrics, result) 49 | return result 50 | } 51 | 52 | func metricName(suffix string) string { 53 | return fmt.Sprintf("portieris_pod_admission_decision_%s", suffix) 54 | } 55 | 56 | func metricHelp(desc string) string { 57 | return fmt.Sprintf("Portieris count of decision outcomes of %s", desc) 58 | } 59 | 60 | // UnregisterAll is used by the unit tests to clean up all metrics, 61 | // registered with Prometheus, at the end of a test run. 62 | func (p *PortierisMetrics) UnregisterAll() { 63 | for _, met := range p.allMetrics { 64 | prometheus.Unregister(met) 65 | } 66 | p.allMetrics = p.allMetrics[:0] 67 | } 68 | 69 | // GetMetricsHandler is used by the unit tests to retrieve the http.Handler 70 | // that Prometheus provides for retrieving metrics 71 | func (p *PortierisMetrics) GetMetricsHandler() http.Handler { 72 | return promhttp.Handler() 73 | } 74 | -------------------------------------------------------------------------------- /pkg/metrics/metrics_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package metrics 16 | 17 | import ( 18 | "io" 19 | "net/http" 20 | "net/http/httptest" 21 | "strconv" 22 | "strings" 23 | "testing" 24 | 25 | "github.com/prometheus/client_golang/prometheus" 26 | "github.com/stretchr/testify/assert" 27 | ) 28 | 29 | // return value of the given metric (or error if not present in metrics) 30 | func getMetric(m *PortierisMetrics, metricName string) (string, error) { 31 | var req *http.Request 32 | var err error 33 | var line []byte 34 | var result string 35 | 36 | if req, err = http.NewRequest("GET", "/metrics", nil); err != nil { 37 | return result, err 38 | } 39 | rr := httptest.NewRecorder() 40 | m.GetMetricsHandler().ServeHTTP(rr, req) 41 | 42 | for err == nil { 43 | line, err = rr.Body.ReadBytes('\n') 44 | lineString := string(line) 45 | if strings.HasPrefix(lineString, metricName) { 46 | result = strings.TrimSpace(strings.Trim(lineString, metricName)) 47 | } 48 | } 49 | if err == io.EOF { 50 | err = nil 51 | } 52 | 53 | return result, err 54 | } 55 | 56 | type MockRegisterer struct { 57 | numRegistered int 58 | } 59 | 60 | func (mr *MockRegisterer) Register(c prometheus.Collector) error { 61 | mr.numRegistered++ 62 | return nil 63 | } 64 | 65 | func (mr *MockRegisterer) MustRegister(c ...prometheus.Collector) { 66 | mr.numRegistered += len(c) 67 | } 68 | 69 | func (mr *MockRegisterer) Unregister(c prometheus.Collector) bool { 70 | mr.numRegistered-- 71 | return true 72 | } 73 | 74 | func TestMetricRegisterUnregister(t *testing.T) { 75 | r := &MockRegisterer{} 76 | dr := prometheus.DefaultRegisterer 77 | defer func() { 78 | prometheus.DefaultRegisterer = dr 79 | }() 80 | prometheus.DefaultRegisterer = r 81 | 82 | m := NewMetrics() 83 | assert.True(t, r.numRegistered > 0) 84 | 85 | m.UnregisterAll() 86 | assert.Equal(t, 0, r.numRegistered) 87 | } 88 | 89 | func TestAllowDecisionMetric(t *testing.T) { 90 | 91 | pm := NewMetrics() 92 | defer pm.UnregisterAll() 93 | pm.AllowDecisionCount.Inc() 94 | metric, metricErr := getMetric(pm, "portieris_pod_admission_decision_allow_count") 95 | 96 | assert.Nil(t, metricErr) 97 | 98 | assert.NotEqual(t, "", metric) 99 | value, err := strconv.Atoi(metric) 100 | assert.Nil(t, err) 101 | assert.NotZero(t, value) 102 | } 103 | 104 | func TestDenyDecisionMetric(t *testing.T) { 105 | 106 | pm := NewMetrics() 107 | defer pm.UnregisterAll() 108 | pm.DenyDecisionCount.Inc() 109 | metric, metricErr := getMetric(pm, "portieris_pod_admission_decision_deny_count") 110 | 111 | assert.Nil(t, metricErr) 112 | 113 | assert.NotEqual(t, "", metric) 114 | value, err := strconv.Atoi(metric) 115 | assert.Nil(t, err) 116 | assert.NotZero(t, value) 117 | } 118 | -------------------------------------------------------------------------------- /pkg/notary/notary_suite_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package notary 16 | 17 | import ( 18 | "os" 19 | 20 | . "github.com/onsi/ginkgo" 21 | . "github.com/onsi/gomega" 22 | 23 | "testing" 24 | ) 25 | 26 | func TestNotary(t *testing.T) { 27 | RegisterFailHandler(Fail) 28 | RunSpecs(t, "Notary Suite") 29 | } 30 | 31 | var ( 32 | trustDir = ".trustTests" 33 | ) 34 | 35 | var _ = AfterSuite(func() { 36 | // Remove test files 37 | os.RemoveAll(trustDir) 38 | }) 39 | -------------------------------------------------------------------------------- /pkg/notary/notary_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package notary 16 | 17 | import ( 18 | . "github.com/onsi/ginkgo" 19 | . "github.com/onsi/gomega" 20 | ) 21 | 22 | var _ = Describe("Notary", func() { 23 | var ( 24 | trust Interface 25 | ) 26 | 27 | BeforeEach(func() { 28 | trust, _ = NewClient(trustDir, nil) 29 | }) 30 | 31 | Describe("Getting the notary repo", func() { 32 | It("should return an error", func() { 33 | _, err := trust.GetNotaryRepo("server", "image", "notaryToken") 34 | Expect(err).To(HaveOccurred()) 35 | Expect(err.Error()).To(ContainSubstring("HTTPStore requires an absolute baseURL")) 36 | }) 37 | }) 38 | 39 | }) 40 | -------------------------------------------------------------------------------- /pkg/registry/fakeregistry/fakeregistry.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018, 2022 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package fakeregistry 16 | 17 | import ( 18 | "fmt" 19 | "sync" 20 | 21 | "github.com/IBM/portieris/pkg/registry" 22 | ) 23 | 24 | var _ registry.Interface = &FakeRegistry{} 25 | 26 | // FakeRegistry . 27 | type FakeRegistry struct { 28 | GetContentTrustTokenStub func(oauthEndpoint string, username string, password string, service string, scope string) (string, error) 29 | getContentTrustTokenMutex sync.RWMutex 30 | getContentTrustTokenArgsForCall []struct { 31 | oauthEndpoint string 32 | username string 33 | password string 34 | service string 35 | scope string 36 | } 37 | getContentTrustTokenReturns struct { 38 | token string 39 | err error 40 | } 41 | } 42 | 43 | // GetContentTrustToken ... 44 | func (fake *FakeRegistry) GetContentTrustToken(oauthEndpoint string, username string, password string, service string, scope string) (string, error) { 45 | fake.getContentTrustTokenMutex.Lock() 46 | fake.getContentTrustTokenArgsForCall = append(fake.getContentTrustTokenArgsForCall, struct { 47 | oauthEndpoint string 48 | username string 49 | password string 50 | service string 51 | scope string 52 | }{oauthEndpoint, username, password, service, scope}) 53 | fake.getContentTrustTokenMutex.Unlock() 54 | if fake.GetContentTrustTokenStub != nil { 55 | return fake.GetContentTrustTokenStub(oauthEndpoint, username, password, service, scope) 56 | } 57 | return fake.getContentTrustTokenReturns.token, fake.getContentTrustTokenReturns.err 58 | } 59 | 60 | // NoAnonymousContentTrustTokenStub ... 61 | func (fake *FakeRegistry) NoAnonymousContentTrustTokenStub(oauthEndpoint string, username string, password string, service string, scope string) (string, error) { 62 | if username == "" { 63 | return "", fmt.Errorf("not allowed") 64 | } 65 | return fake.getContentTrustTokenReturns.token, fake.getContentTrustTokenReturns.err 66 | } 67 | 68 | // GetContentTrustTokenReturns ... 69 | func (fake *FakeRegistry) GetContentTrustTokenReturns(token string, err error) { 70 | fake.getContentTrustTokenMutex.Lock() 71 | defer fake.getContentTrustTokenMutex.Unlock() 72 | fake.getContentTrustTokenReturns = struct { 73 | token string 74 | err error 75 | }{token, err} 76 | } 77 | 78 | // GetRegistryToken ... 79 | func (fake *FakeRegistry) GetRegistryToken(oauthEndpoint string, username string, password string, service string, scope string) (string, error) { 80 | return "", fmt.Errorf("not implemented") 81 | } 82 | -------------------------------------------------------------------------------- /pkg/registry/registry.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018, 2022 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package registry 16 | 17 | import ( 18 | "github.com/IBM/portieris/helpers/oauth" 19 | ) 20 | 21 | // Client . 22 | type Client struct{} 23 | 24 | // Interface . 25 | type Interface interface { 26 | GetContentTrustToken(oauthEndpoint string, username string, password string, service string, scope string) (string, error) 27 | GetRegistryToken(oauthEndpoint string, username string, password string, service string, scope string) (string, error) 28 | } 29 | 30 | // NewClient . 31 | func NewClient() Interface { 32 | return &Client{} 33 | } 34 | 35 | // GetContentTrustToken . 36 | func (c Client) GetContentTrustToken(oauthEndpoint string, username string, password string, service string, scope string) (string, error) { 37 | if username == "" && password == "" { 38 | return "", nil 39 | } 40 | 41 | token, err := oauth.Request(oauthEndpoint, username, password, service, scope) 42 | if err != nil { 43 | return "", err 44 | } 45 | return token.Token, nil 46 | } 47 | 48 | // GetRegistryToken . 49 | func (c Client) GetRegistryToken(oauthEndpoint string, username string, password string, service string, scope string) (string, error) { 50 | token, err := oauth.Request(oauthEndpoint, username, password, "registry", scope) 51 | if err != nil { 52 | return "", err 53 | } 54 | return token.Token, nil 55 | } 56 | -------------------------------------------------------------------------------- /pkg/verifier/simple/gpgArmour.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package simple 16 | 17 | import ( 18 | "bytes" 19 | "fmt" 20 | "io/ioutil" 21 | 22 | "golang.org/x/crypto/openpgp/armor" 23 | ) 24 | 25 | func decodeArmoredKey(keyBytes []byte) ([]byte, error) { 26 | if len(keyBytes) == 0 { 27 | return nil, fmt.Errorf("Key: empty") 28 | } 29 | block, err := armor.Decode(bytes.NewReader(keyBytes)) 30 | if err != nil { 31 | if err.Error() == "EOF" { 32 | return nil, fmt.Errorf("Unable to decode key: %v", err) 33 | } 34 | return nil, err 35 | } 36 | switch block.Type { 37 | case "PGP PUBLIC KEY BLOCK": 38 | break 39 | default: 40 | return nil, fmt.Errorf("Expected \"PGP PUBLIC KEY BLOCK\" not found: %s", block.Type) 41 | } 42 | return ioutil.ReadAll(block.Body) 43 | } 44 | -------------------------------------------------------------------------------- /pkg/verifier/simple/sigStoreConfig.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package simple 16 | 17 | import ( 18 | "fmt" 19 | "io/ioutil" 20 | "os" 21 | "strings" 22 | 23 | "github.com/golang/glog" 24 | "gopkg.in/yaml.v3" 25 | ) 26 | 27 | type regConfig struct { 28 | SigStore string `yaml:"sigstore"` 29 | } 30 | type config struct { 31 | DefaultDocker regConfig `yaml:"default-docker"` 32 | } 33 | 34 | // CreateRegistryDir write a file in a new directory containing the desired default docker configuration 35 | func (v verifier) CreateRegistryDir(storeURL, storeUser, storePassword string) (string, error) { 36 | if storeURL == "" { 37 | glog.Infof("No lookaside signature store.") 38 | return "", nil 39 | } 40 | method := "" 41 | rest := "" 42 | if strings.HasPrefix(storeURL, "http://") { 43 | method = "http://" 44 | rest = strings.TrimPrefix(storeURL, "http://") 45 | } 46 | if strings.HasPrefix(storeURL, "https://") { 47 | method = "https://" 48 | rest = strings.TrimPrefix(storeURL, "https://") 49 | } 50 | 51 | // allow only http:// and https:// 52 | if method == "" { 53 | return "", fmt.Errorf("expecting https:// or http:// URL, got: %s", storeURL) 54 | } 55 | // insert credentials as user:password@ 56 | if storeUser == "" { 57 | glog.Infof("Lookaside signature store at: %s", storeURL) 58 | } else { 59 | glog.Infof("Lookaside signature store at: %s%s:***@%s", method, storeUser, rest) 60 | storeURL = fmt.Sprintf("%s%s:%s@%s", method, storeUser, storePassword, rest) 61 | } 62 | 63 | dir, err := ioutil.TempDir("", "registry.d") 64 | if err != nil { 65 | return "", err 66 | } 67 | file, err := os.OpenFile(dir+"/default.yaml", os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600) 68 | if err != nil { 69 | os.RemoveAll(dir) 70 | return "", err 71 | } 72 | 73 | rConf := config{ 74 | DefaultDocker: regConfig{ 75 | SigStore: storeURL, 76 | }, 77 | } 78 | bytes, err := yaml.Marshal(rConf) 79 | if err != nil { 80 | os.RemoveAll(dir) 81 | return "", err 82 | } 83 | 84 | _, err = file.Write(bytes) 85 | if err != nil { 86 | os.RemoveAll(dir) 87 | return "", err 88 | } 89 | glog.Infof("Using store config dir: %s", dir) 90 | return dir, nil 91 | } 92 | 93 | // RemoveRegistryDir . 94 | func (v verifier) RemoveRegistryDir(dirName string) error { 95 | if dirName == "" { 96 | return nil 97 | } 98 | return os.RemoveAll(dirName) 99 | } 100 | -------------------------------------------------------------------------------- /pkg/verifier/simple/verifier.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018, 2021 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package simple 16 | 17 | import ( 18 | "bytes" 19 | 20 | "github.com/IBM/portieris/helpers/credential" 21 | policyv1 "github.com/IBM/portieris/pkg/apis/portieris.cloud.ibm.com/v1" 22 | "github.com/IBM/portieris/pkg/kubernetes" 23 | "github.com/containers/image/v5/signature" 24 | ) 25 | 26 | // Verifier is for verifying simple signing 27 | type Verifier interface { 28 | TransformPolicies(kWrapper kubernetes.WrapperInterface, namespace string, inPolicies []policyv1.SimpleRequirement) (*signature.Policy, error) 29 | CreateRegistryDir(storeURL, storeUser, storePassword string) (string, error) 30 | VerifyByPolicy(imageToVerify string, credentials credential.Credentials, registriesConfigDir string, simplePolicy *signature.Policy) (*bytes.Buffer, error, error) 31 | RemoveRegistryDir(dirName string) error 32 | } 33 | 34 | type verifier struct{} 35 | 36 | // NewVerifier creates a new Verfier 37 | func NewVerifier() Verifier { 38 | return &verifier{} 39 | } 40 | -------------------------------------------------------------------------------- /pkg/verifier/vulnerability/iam.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020, 2023 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package vulnerability 16 | 17 | import ( 18 | "net/http" 19 | "sync" 20 | 21 | ibmcloud "github.com/IBM/go-sdk-core/v5/core" 22 | ) 23 | 24 | type authenticatorCache struct { 25 | Map sync.Map 26 | } 27 | 28 | var authenticators *authenticatorCache 29 | var once sync.Once 30 | 31 | func getAuthenticatorCache() *authenticatorCache { 32 | if authenticators == nil { 33 | once.Do( 34 | func() { 35 | authenticators = &authenticatorCache{} 36 | }) 37 | } 38 | return authenticators 39 | } 40 | 41 | type requestAuthenticator interface { 42 | Authenticate(request *http.Request) error 43 | } 44 | 45 | type authenticatorFactoryInterface interface { 46 | NewAuthenticator(string) (requestAuthenticator, error) 47 | } 48 | 49 | type authenticatorFactory struct{} 50 | 51 | func (a authenticatorFactory) NewAuthenticator(apiKey string) (requestAuthenticator, error) { 52 | return ibmcloud.NewIamAuthenticatorBuilder().SetApiKey(apiKey).Build() 53 | } 54 | -------------------------------------------------------------------------------- /pkg/verifier/vulnerability/iam_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package vulnerability 16 | 17 | import ( 18 | "sync" 19 | "testing" 20 | 21 | "github.com/stretchr/testify/assert" 22 | ) 23 | 24 | func Test_getAuthenticatorCache(t *testing.T) { 25 | // we should always get the same memory address back from getAuthenticatorCache 26 | threads := 100 27 | addresses := make([]*authenticatorCache, 0, threads) 28 | wg := sync.WaitGroup{} 29 | wg.Add(threads) 30 | for i := 0; i < threads; i++ { 31 | go func() { 32 | defer wg.Done() 33 | cache := getAuthenticatorCache() 34 | addresses = append(addresses, cache) 35 | }() 36 | } 37 | wg.Wait() 38 | 39 | unique := map[*authenticatorCache]bool{} 40 | 41 | for _, c := range addresses { 42 | unique[c] = true 43 | } 44 | 45 | assert.Equal(t, 1, len(unique)) 46 | } 47 | -------------------------------------------------------------------------------- /pkg/verifier/vulnerability/vulnerability.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020, 2021 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package vulnerability 16 | 17 | import ( 18 | "github.com/IBM/portieris/helpers/credential" 19 | "github.com/IBM/portieris/helpers/image" 20 | policyv1 "github.com/IBM/portieris/pkg/apis/portieris.cloud.ibm.com/v1" 21 | "github.com/golang/glog" 22 | ) 23 | 24 | // Scanner is an interface for vulnerability scanner implementations 25 | type Scanner interface { 26 | CanImageDeployBasedOnVulnerabilities(image.Reference) (ScanResponse, error) 27 | } 28 | 29 | // ScanResponse is a struct for vulnerability scanners to return 30 | type ScanResponse struct { 31 | CanDeploy bool 32 | DenyReason string 33 | } 34 | 35 | // ScannerFactory is the interface for a ScannerFactory, supports testing 36 | type ScannerFactory interface { 37 | GetScanners(image.Reference, credential.Credentials, policyv1.Policy) []Scanner 38 | } 39 | 40 | // DefaultScannerFactory is the defaul implementation of ScannerFactory 41 | type DefaultScannerFactory struct{} 42 | 43 | // NewScannerFactory returns a new DefaultScannerFactory 44 | func NewScannerFactory() DefaultScannerFactory { 45 | return DefaultScannerFactory{} 46 | } 47 | 48 | // GetScanners returns a slice of suitable Scanners based on the provided policy 49 | func (f *DefaultScannerFactory) GetScanners(img image.Reference, credentials credential.Credentials, policy policyv1.Policy) (scanners []Scanner) { 50 | if policy.Vulnerability.ICCRVA.Enabled != nil && *policy.Vulnerability.ICCRVA.Enabled { 51 | glog.Infof("vulnerability: Using Vulnerability Advisor for IBM Cloud Container Registry for image %q.", img.String()) 52 | scanners = append(scanners, NewIBMVulnerabilityAdvisorScanner(credentials, policy.Vulnerability.ICCRVA.Account)) 53 | } 54 | 55 | return 56 | } 57 | -------------------------------------------------------------------------------- /pkg/verifier/vulnerability/vulnerability_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020, 2021 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package vulnerability 16 | 17 | import ( 18 | "testing" 19 | 20 | "github.com/IBM/portieris/helpers/credential" 21 | "github.com/IBM/portieris/helpers/image" 22 | policyv1 "github.com/IBM/portieris/pkg/apis/portieris.cloud.ibm.com/v1" 23 | 24 | "github.com/stretchr/testify/assert" 25 | "github.com/stretchr/testify/require" 26 | ) 27 | 28 | func Test_NewScannerFactory(t *testing.T) { 29 | f := NewScannerFactory() 30 | assert.Equal(t, DefaultScannerFactory{}, f) 31 | } 32 | 33 | func boolToPointer(in bool) *bool { 34 | return &in 35 | } 36 | 37 | func Test_GetScanners(t *testing.T) { 38 | f := NewScannerFactory() 39 | 40 | tests := []struct { 41 | name string 42 | policy policyv1.Policy 43 | credentials credential.Credentials 44 | wantScanners []Scanner 45 | }{ 46 | { 47 | name: "Returns no scanners when none in policy", 48 | policy: policyv1.Policy{}, 49 | }, 50 | { 51 | name: "Returns Vulnerability Advisor for IBM Cloud Container Registry scanner if enabled", 52 | policy: policyv1.Policy{ 53 | Vulnerability: policyv1.Vulnerability{ 54 | ICCRVA: policyv1.ICCRVA{ 55 | Enabled: boolToPointer(true), 56 | }, 57 | }, 58 | }, 59 | wantScanners: []Scanner{ 60 | &ICCRVAScanner{}, 61 | }, 62 | }, 63 | { 64 | name: "Account in ICCRVA policy is correctly set on scanner", 65 | policy: policyv1.Policy{ 66 | Vulnerability: policyv1.Vulnerability{ 67 | ICCRVA: policyv1.ICCRVA{ 68 | Enabled: boolToPointer(true), 69 | Account: "123"}, 70 | }, 71 | }, 72 | wantScanners: []Scanner{ 73 | &ICCRVAScanner{AccountHeader: "123"}, 74 | }, 75 | }, 76 | } 77 | 78 | for _, test := range tests { 79 | t.Run(test.name, func(t *testing.T) { 80 | img, err := image.NewReference("icr.io/sam/ida") 81 | require.NoError(t, err) 82 | 83 | gotScanners := f.GetScanners(*img, test.credentials, test.policy) 84 | 85 | assert.Equal(t, len(test.wantScanners), len(gotScanners)) 86 | for idx, wantScanner := range test.wantScanners { 87 | gotScanner := gotScanners[idx] 88 | assert.IsType(t, wantScanner, gotScanner) 89 | 90 | switch s := gotScanner.(type) { 91 | case *ICCRVAScanner: 92 | // ICCRVA scanner specific checks 93 | wantAccount := test.policy.Vulnerability.ICCRVA.Account 94 | gotAccount := s.AccountHeader 95 | assert.Equal(t, wantAccount, gotAccount) 96 | } 97 | } 98 | }) 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /pkg/webhook/responder_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package webhook 16 | 17 | import ( 18 | "fmt" 19 | "testing" 20 | 21 | "github.com/stretchr/testify/assert" 22 | ) 23 | 24 | func TestAdmissionResponder(t *testing.T) { 25 | t.Run("should include the error in the response", func(t *testing.T) { 26 | responder := &AdmissionResponder{} 27 | err := fmt.Errorf("FAKE_ERROR") 28 | responder.ToAdmissionResponse(err) 29 | resp := responder.Flush() 30 | assert.Equal(t, "\nFAKE_ERROR", resp.Result.Message) 31 | assert.False(t, resp.Allowed) 32 | }) 33 | 34 | t.Run("should include the patches in the response", func(t *testing.T) { 35 | responder := &AdmissionResponder{} 36 | patch := []byte("patch") 37 | responder.SetPatch(patch) 38 | responder.SetAllowed() 39 | resp := responder.Flush() 40 | assert.Equal(t, string(patch), string(resp.Patch)) 41 | assert.True(t, resp.Allowed) 42 | }) 43 | } 44 | -------------------------------------------------------------------------------- /scripts/copyright-check.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | CRC=0 4 | 5 | RED='\033[0;31m' 6 | NC='\033[0m' # No Color 7 | 8 | function fail() 9 | { 10 | echo -en $RED 11 | tput bold 12 | echo -e "$1" 13 | tput sgr0 14 | echo -en $NC 15 | } 16 | 17 | 18 | # Enforce check for copyright statements in Go code 19 | GOSRCFILES=($(find . -name "*.go" | grep -v code-generator)) 20 | THISYEAR=$(date +"%Y") 21 | for GOFILE in "${GOSRCFILES[@]}"; do 22 | if ! grep -q "Licensed under the Apache License, Version 2.0" $GOFILE; then 23 | fail "Missing copyright/licence statement in ${GOFILE}." 24 | CRC=$(($CRC + 1)) 25 | fi 26 | done 27 | 28 | # Check for multi-line in new files 29 | if git fetch origin ; then 30 | GOSRCFILES=($(git diff --name-only origin/main | grep '\.go$')) 31 | for GOFILE in "${GOSRCFILES[@]}"; do 32 | if grep -q "Copyright .* Portieris Authors." $GOFILE; then 33 | YEAR_LINE=$(grep "Copyright .* Portieris Authors." $GOFILE) 34 | YEARS=($(echo $YEAR_LINE | grep -oE '[0-9]{4}')) 35 | if [[ ${#YEARS[@]} == 1 ]]; then 36 | if [[ ${YEARS[0]} != ${THISYEAR} ]]; then 37 | fail "Single out-of-date copyright in ${GOFILE}." 38 | CRC=$(($CRC + 1)) 39 | fi 40 | elif [[ ${#YEARS[@]} == 2 ]]; then 41 | if [[ ${YEARS[1]} != ${THISYEAR} ]]; then 42 | fail "Double year copyright with out-of-date second year in ${GOFILE}." 43 | CRC=$(($CRC + 1)) 44 | fi 45 | else 46 | echo "#YEARS was ${#YEARS[@]}" 47 | fi 48 | fi 49 | done 50 | if [ $CRC -gt 0 ]; then fail "Please run make copyright to add copyright statements and check in the updated file(s).\n"; fi 51 | else 52 | fail "Failed to get branch information for origin/main, cannot perform copyright check. Make sure that you have a remote called origin in your git project." 53 | CRC=$(($CRC + 1)) 54 | fi 55 | exit $CRC 56 | -------------------------------------------------------------------------------- /scripts/copyright.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | set +x 4 | 5 | GOSRCFILES=($(find . -name "*.go")) 6 | THISYEAR=$(date +"%Y") 7 | 8 | for GOFILE in "${GOSRCFILES[@]}"; do 9 | # If no copyright at all 10 | if ! grep -q "Licensed under the Apache License, Version 2.0" $GOFILE; then 11 | echo "// Copyright ${THISYEAR} Portieris Authors. 12 | // 13 | // Licensed under the Apache License, Version 2.0 (the \"License\"); 14 | // you may not use this file except in compliance with the License. 15 | // You may obtain a copy of the License at 16 | // 17 | // http://www.apache.org/licenses/LICENSE-2.0 18 | // 19 | // Unless required by applicable law or agreed to in writing, software 20 | // distributed under the License is distributed on an \"AS IS\" BASIS, 21 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 22 | // See the License for the specific language governing permissions and 23 | // limitations under the License. 24 | 25 | $(cat ${GOFILE})" > ${GOFILE} 26 | fi 27 | done 28 | 29 | # Check for multi-line in new files 30 | GOSRCFILES=($(git diff --name-only origin/main | grep '\.go$')) 31 | for GOFILE in "${GOSRCFILES[@]}"; do 32 | if grep -q "Copyright .* Portieris Authors." $GOFILE; then 33 | YEAR_LINE=$(grep "Copyright .* Portieris Authors." $GOFILE) 34 | YEARS=($(echo $YEAR_LINE | grep -oE '\d{4}')) 35 | if [[ ${#YEARS[@]} == 1 ]]; then 36 | if [[ ${YEARS[0]} != ${THISYEAR} ]]; then 37 | sed -i '' -e "s|Copyright ${YEARS[0]} Portieris Authors.|Copyright ${YEARS[0]}, ${THISYEAR} Portieris Authors.|" $GOFILE 38 | fi 39 | elif [[ ${#YEARS[@]} == 2 ]]; then 40 | if [[ ${YEARS[1]} != ${THISYEAR} ]]; then 41 | sed -i '' -e "s|Copyright ${YEARS[0]}, ${YEARS[1]} Portieris Authors.|Copyright ${YEARS[0]}, ${THISYEAR} Portieris Authors.|" $GOFILE 42 | fi 43 | fi 44 | fi 45 | done 46 | -------------------------------------------------------------------------------- /scripts/env.sh: -------------------------------------------------------------------------------- 1 | # probably set these to the same user APIKEY 2 | export PORTIERIS_PULL_APIKEY= 3 | export PORTIERIS_TESTIMAGE_APIKEY= 4 | # charts to test 5 | export VERSION= 6 | # image tag to test e.g. prep-v0.13.19 7 | export TAG= 8 | 9 | # name of the secret used to pull portieris made from $REG and $PORTIERIS_PULL_APIKEY 10 | export PULLSECRET=portieris-test 11 | export REG=icr.io 12 | export HUB=${REG}/registry-deploy 13 | 14 | # points to kube tests cluster (docker) 15 | export KUBECONFIG=~/.kube/config 16 | -------------------------------------------------------------------------------- /scripts/install-on-docker: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | 4 | # make a secret we can use to pull the candidate image 5 | kubectl create secret docker-registry ${PULLSECRET} --docker-username iamapikey --docker-password "${PORTIERIS_PULL_APIKEY}" --docker-server ${REG} 6 | # make a secret that e2e tests can use to pull test images, do notary and get va results 7 | # e2e tests copy this to the test namespaces 8 | kubectl create secret docker-registry all-icr-io --docker-username iamapikey --docker-password "${PORTIERIS_TESTIMAGE_APIKEY}" --docker-server icr.io 9 | 10 | kubectl create ns portieris 11 | kubectl get secret ${PULLSECRET} -o yaml | sed 's/namespace: default/namespace: portieris/' | kubectl create -f - 12 | kubectl cluster-info 13 | set -x 14 | helm install -n portieris portieris portieris-${VERSION}.tgz --set image.host=${HUB} --set image.tag=${TAG} --set image.pullSecret=${PULLSECRET} --set IBMContainerService=false 15 | set +x 16 | 17 | sleep 15 18 | kubectl get pods -n portieris 19 | 20 | -------------------------------------------------------------------------------- /scripts/makeKeySecret.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Write a Kubernetes secret containing public key(s) for Portieris to verify simple signatures 4 | 5 | NAME=$1 6 | if [ -z "${NAME}" ];then 7 | echo usage: $0 ' [] []' 8 | echo 'where can be obtained from `gpg -k --key-id-format=long`' 9 | fi 10 | shift 11 | 12 | if [ "$*" == "" ]; then 13 | echo exporting all public keys 14 | fi 15 | 16 | cat < ${NAME}.yaml 17 | apiVersion: v1 18 | kind: Secret 19 | type: Opaque 20 | metadata: 21 | name: ${NAME} 22 | stringData: 23 | key: | 24 | EOF 25 | gpg --export --armour $* | sed 's/^/ /' >> ${NAME}.yaml 26 | -------------------------------------------------------------------------------- /scripts/makeTest.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | GOTAGS=$2 4 | 5 | failures=0 6 | echo 'mode: atomic' > cover.out 7 | for PACKAGE in $1; do 8 | go test --tags $GOTAGS -covermode=atomic -coverprofile=cover.tmp $PACKAGE && tail -n +2 cover.tmp >> cover.out 9 | packageResult=$? 10 | failures=$((failures + $packageResult)) 11 | 12 | if [[ $packageResult > 0 ]]; then 13 | echo "At least one test failed in package $PACKAGE" 14 | fi 15 | 16 | rm -f cover.tmp 17 | done 18 | 19 | if [[ $failures > 0 ]]; then 20 | echo "$failures package has failed tests" 21 | exit 1 22 | else 23 | echo "all tests pass" 24 | fi 25 | -------------------------------------------------------------------------------- /scripts/uninstall-on-docker: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | helm delete -n portieris portieris 4 | kubectl delete namespace portieris 5 | kubectl delete secret portieris-test 6 | kubectl delete secret all-icr-io 7 | -------------------------------------------------------------------------------- /test/e2e/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | copyright: 4 | years: 2020, 2021 5 | lastupdated: "2021-08-26" 6 | 7 | --- 8 | 9 | # E2E tests 10 | 11 | The end-to-end (E2E) tests run against a Kubernetes cluster. The tests are typically run by maintainers against an IBM Cloud Kubernetes Service cluster before a pull request (PR) is accepted. 12 | 13 | Before you begin, contact a maintainer to find out what to set for exporting the `E2E_ACCOUNT_HEADER`. You'll need this information for one of the steps. 14 | 15 | Run a test by completing the following steps. 16 | 17 | 1. Order an IBM Cloud Kubernetes Service cluster, see [Creating clusters](https://cloud.ibm.com/docs/containers?topic=containers-clusters), or use an existing cluster. 18 | 2. Get the cluster configuration by running the following [`ibmcloud ks cluster config`](https://cloud.ibm.com/docs/containers?topic=containers-kubernetes-service-cli#cs_cluster_config) command. 19 | 20 | ``` 21 | ibmcloud ks cluster config -c 22 | ``` 23 | 24 | 3. Export KUBECONFIG to point at the `kube-config.yaml` file for your cluster that is in `~/.bluemix/plugins/container-service/clusters`. 25 | 26 | For example, 27 | 28 | ``` 29 | export KUBECONFIG=~/.bluemix/plugins/container-service/clusters/mycluster-xxxx/kube-config.yaml 30 | ``` 31 | 32 | 5. Create an IBM Cloud Container Registry namespace that is owned by the same account as the cluster, see [Setting up a namespace](https://cloud.ibm.com/docs/Registry?topic=Registry-registry_setup_cli_namespace#registry_namespace_setup). 33 | 6. Export the `HUB` variable that points to your IBM Cloud Container Registry namespace. 34 | 35 | For example, 36 | 37 | ``` 38 | export HUB=uk.icr.io/yournamespace 39 | ``` 40 | 41 | 8. After completing your code changes, build and push the image to your namespace by running the following command. 42 | 43 | ``` 44 | make push 45 | ``` 46 | 47 | 8. Install Portieris into your cluster. 48 | 49 | ``` 50 | helm install helm/portieris 51 | ``` 52 | 53 | 11. Export `E2E_ACCOUNT_HEADER`. (Contact a maintainer to find out what to set.) 54 | 12. Run the following command. 55 | 56 | ``` 57 | make e2e.quick 58 | ``` 59 | -------------------------------------------------------------------------------- /test/e2e/ibmcontainerservice.helm_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package e2e 16 | 17 | import ( 18 | "testing" 19 | 20 | "github.com/IBM/portieris/test/e2e/utils" 21 | 22 | "github.com/stretchr/testify/assert" 23 | ) 24 | 25 | func TestIBMContainerService_KubeSystem(t *testing.T) { 26 | utils.CheckIfTesting(t, testArmada) 27 | t.Run("Run anything in kube-system", func(t *testing.T) { 28 | utils.TestDeploymentRunnable(t, framework, "./testdata/deployment/dockerhub-nginx-unsigned.yaml", "kube-system") 29 | }) 30 | 31 | t.Run("Patch existing ICS deployment with annotation, verify it still works", func(t *testing.T) { 32 | testAnnotation := framework.GenerateTestAnnotation() 33 | deployment, err := framework.PatchDeployment("kubernetes-dashboard", "kube-system", testAnnotation) 34 | if err != nil { 35 | t.Fatalf("Error patching Kube Dashboard deployment: %v", err) 36 | } 37 | deployment, err = framework.GetDeployment(deployment.Name, "kube-system") 38 | if err != nil { 39 | t.Fatalf("Error refreshing Kube Dashboard deployment: %v", err) 40 | } 41 | assert.Equal(t, deployment.Status.AvailableReplicas, *deployment.Spec.Replicas, "Deployment available replicas did not match expected replicas") 42 | assert.Equal(t, deployment.Status.UpdatedReplicas, *deployment.Spec.Replicas, "Deployment updated replicas did not match expected replicas") 43 | if err = framework.DeleteDeployment(deployment.Name, deployment.Namespace); err != nil { 44 | t.Fatalf("Error deleting kube-dashboard deployment: %v", err) 45 | } 46 | }) 47 | } 48 | 49 | func TestIBMContainerService_IBMSystem(t *testing.T) { 50 | utils.CheckIfTesting(t, testArmada) 51 | t.Run("Run anything in ibm-system", func(t *testing.T) { 52 | utils.TestDeploymentRunnable(t, framework, "./testdata/deployment/dockerhub-nginx-unsigned.yaml", "ibm-system") 53 | }) 54 | } 55 | -------------------------------------------------------------------------------- /test/e2e/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package e2e 16 | 17 | // This is here to avoid breaking of godoc 18 | -------------------------------------------------------------------------------- /test/e2e/notary.ibm.clusterimagepolicy_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018, 2023 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package e2e 16 | 17 | import ( 18 | "testing" 19 | 20 | "github.com/IBM/portieris/test/e2e/utils" 21 | ) 22 | 23 | // Temporary check until other registries are supported. 24 | func TestNotary_ClusterImagePolicyRepositories_ThirdPartyTrust(t *testing.T) { 25 | utils.CheckIfTesting(t, testTrustClusterImagePolicy) 26 | t.Run("Third party trust is rejected", func(t *testing.T) { 27 | clusterImagePolicy, namespace := utils.CreateClusterImagePolicyAndNamespace(t, framework, "./testdata/clusterimagepolicy/allow-signed-dockerhub.yaml") 28 | utils.TestDeploymentNotRunnable(t, framework, "./testdata/deployment/dockerhub-nginx-unsigned.yaml", namespace.Name) 29 | utils.CleanUpClusterImagePolicyTest(t, framework, clusterImagePolicy.Name, namespace.Name) 30 | }) 31 | } 32 | -------------------------------------------------------------------------------- /test/e2e/notary.ibm.imagepolicy_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018, 2023 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package e2e 16 | 17 | import ( 18 | "testing" 19 | 20 | "github.com/IBM/portieris/test/e2e/utils" 21 | ) 22 | 23 | // Temporary check until other registries are supported. 24 | func TestNotary_ImagePolicyRepositories_ThirdPartyTrust(t *testing.T) { 25 | utils.CheckIfTesting(t, testTrustImagePolicy) 26 | t.Run("Third party trust is rejected", func(t *testing.T) { 27 | t.Parallel() 28 | namespace := utils.CreateImagePolicyInstalledNamespace(t, framework, "./testdata/imagepolicy/allow-signed-dockerhub.yaml", "") 29 | utils.TestDeploymentNotRunnable(t, framework, "./testdata/deployment/dockerhub-nginx-unsigned.yaml", namespace.Name) 30 | utils.CleanUpImagePolicyTest(t, framework, namespace.Name) 31 | }) 32 | } 33 | 34 | func TestNotary_ImagePolicyRepositories_OverridesClusterPolicies(t *testing.T) { 35 | utils.CheckIfTesting(t, testTrustImagePolicy) 36 | t.Run("Unsigned image is rejected if cluster policy allows all but image policy only allows signed", func(t *testing.T) { 37 | t.Parallel() 38 | namespace := utils.CreateImagePolicyInstalledNamespace(t, framework, "./testdata/imagepolicy/allow-signed.yaml", "") 39 | clusterImagePolicy, redundantNamespace := utils.CreateClusterImagePolicyAndNamespace(t, framework, "./testdata/clusterimagepolicy/allow-all.yaml") 40 | utils.TestDeploymentNotRunnable(t, framework, "./testdata/deployment/dockerhub-nginx-unsigned.yaml", namespace.Name) 41 | utils.CleanUpImagePolicyTest(t, framework, namespace.Name) 42 | utils.CleanUpClusterImagePolicyTest(t, framework, clusterImagePolicy.Name, redundantNamespace.Name) 43 | }) 44 | } 45 | -------------------------------------------------------------------------------- /test/e2e/testdata/clusterimagepolicy/allow-all.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: portieris.cloud.ibm.com/v1 2 | kind: ClusterImagePolicy 3 | metadata: 4 | name: allow-all 5 | spec: 6 | repositories: 7 | - name: "*" 8 | policy: {} 9 | -------------------------------------------------------------------------------- /test/e2e/testdata/clusterimagepolicy/allow-signed-dockerhub.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: portieris.cloud.ibm.com/v1 2 | kind: ClusterImagePolicy 3 | metadata: 4 | name: allow-signed 5 | spec: 6 | repositories: 7 | - name: "docker.io/nginx" 8 | policy: 9 | trust: 10 | enabled: true 11 | -------------------------------------------------------------------------------- /test/e2e/testdata/clusterimagepolicy/allow-signed.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: portieris.cloud.ibm.com/v1 2 | kind: ClusterImagePolicy 3 | metadata: 4 | name: allow-signed 5 | spec: 6 | repositories: 7 | - name: "de.icr.io/testing-authn/nginx" 8 | policy: 9 | simple: 10 | requirements: 11 | - type: "signedBy" 12 | keySecret: simple1pubkey 13 | -------------------------------------------------------------------------------- /test/e2e/testdata/clusterimagepolicy/simple-accept-anything.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: portieris.cloud.ibm.com/v1 2 | kind: ClusterImagePolicy 3 | metadata: 4 | name: simple-accept-anything 5 | spec: 6 | repositories: 7 | - name: "*" 8 | policy: 9 | simple: 10 | requirements: 11 | - type: "insecureAcceptAnything" 12 | -------------------------------------------------------------------------------- /test/e2e/testdata/clusterimagepolicy/simple-remap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: portieris.cloud.ibm.com/v1 2 | kind: ClusterImagePolicy 3 | metadata: 4 | name: simple-remap 5 | spec: 6 | repositories: 7 | - name: "icr.io/cise/*" 8 | policy: 9 | simple: 10 | requirements: 11 | - type: "signedBy" 12 | keySecret: simple1pubkey 13 | signedIdentity: 14 | type: "remapIdentity" 15 | prefix: "icr.io/cise/remap" 16 | signedPrefix: "icr.io/cise" 17 | -------------------------------------------------------------------------------- /test/e2e/testdata/clusterimagepolicy/simple-signedby1-keysecret-namespace-override.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: portieris.cloud.ibm.com/v1 2 | kind: ClusterImagePolicy 3 | metadata: 4 | name: simple-signedby1 5 | spec: 6 | repositories: 7 | - name: "icr.io/cise/*" 8 | policy: 9 | simpleStore: 10 | url: "https://foo.com/x" 11 | auth: storesecret 12 | simple: 13 | requirements: 14 | - type: "signedBy" 15 | keySecret: simple1pubkey 16 | keySecretNamespace: secretnamespace 17 | -------------------------------------------------------------------------------- /test/e2e/testdata/clusterimagepolicy/simple-signedby1.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: portieris.cloud.ibm.com/v1 2 | kind: ClusterImagePolicy 3 | metadata: 4 | name: simple-signedby1 5 | spec: 6 | repositories: 7 | - name: "icr.io/cise/*" 8 | policy: 9 | simpleStore: 10 | url: "https://foo.com/x" 11 | auth: storesecret 12 | simple: 13 | requirements: 14 | - type: "signedBy" 15 | keySecret: simple1pubkey 16 | -------------------------------------------------------------------------------- /test/e2e/testdata/clusterimagepolicy/simple-signedby2-mutate.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: portieris.cloud.ibm.com/v1 2 | kind: ClusterImagePolicy 3 | metadata: 4 | name: simple-signedby2-mutate 5 | spec: 6 | repositories: 7 | - name: "icr.io/cise/*" 8 | policy: 9 | mutateImage: true 10 | simple: 11 | requirements: 12 | - type: "signedBy" 13 | keySecret: simple1pubkey 14 | - type: "signedBy" 15 | keySecret: simple2pubkey -------------------------------------------------------------------------------- /test/e2e/testdata/clusterimagepolicy/simple-signedby2-nomutate.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: portieris.cloud.ibm.com/v1 2 | kind: ClusterImagePolicy 3 | metadata: 4 | name: simple-signedby2-nomutate 5 | spec: 6 | repositories: 7 | - name: "icr.io/cise/*" 8 | policy: 9 | mutateImage: false 10 | simple: 11 | requirements: 12 | - type: "signedBy" 13 | keySecret: simple1pubkey 14 | - type: "signedBy" 15 | keySecret: simple2pubkey -------------------------------------------------------------------------------- /test/e2e/testdata/clusterimagepolicy/simple-signedby2.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: portieris.cloud.ibm.com/v1 2 | kind: ClusterImagePolicy 3 | metadata: 4 | name: simple-signedby2 5 | spec: 6 | repositories: 7 | - name: "icr.io/cise/*" 8 | policy: 9 | simple: 10 | requirements: 11 | - type: "signedBy" 12 | keySecret: simple1pubkey 13 | - type: "signedBy" 14 | keySecret: simple2pubkey 15 | -------------------------------------------------------------------------------- /test/e2e/testdata/cronjob/global-nginx-signed.yaml: -------------------------------------------------------------------------------- 1 | # simple signed 2 | apiVersion: batch/v1 3 | kind: Job 4 | metadata: 5 | name: global-nginx-signed 6 | labels: 7 | app: global-nginx-signed 8 | test: true 9 | spec: 10 | schedule: "* * * * *" 11 | jobTemplate: 12 | spec: 13 | template: 14 | spec: 15 | containers: 16 | - name: nginx 17 | image: icr.io/portieris-authn/nginx:signed 18 | ports: 19 | - containerPort: 80 20 | restartPolicy: Never 21 | -------------------------------------------------------------------------------- /test/e2e/testdata/cronjob/global-nginx-unsigned.yaml: -------------------------------------------------------------------------------- 1 | # simple signed 2 | apiVersion: batch/v1 3 | kind: Job 4 | metadata: 5 | name: global-nginx-unsigned 6 | labels: 7 | app: global-nginx-unsigned 8 | test: true 9 | spec: 10 | schedule: "* * * * *" 11 | jobTemplate: 12 | spec: 13 | template: 14 | spec: 15 | containers: 16 | - name: nginx 17 | image: icr.io/portieris-authn/nginx:unsigned 18 | ports: 19 | - containerPort: 80 20 | restartPolicy: Never 21 | -------------------------------------------------------------------------------- /test/e2e/testdata/daemonset/global-nginx-signed.yaml: -------------------------------------------------------------------------------- 1 | # simple signed 2 | apiVersion: apps/v1 3 | kind: DaemonSet 4 | metadata: 5 | name: global-nginx-signed 6 | labels: 7 | app: global-nginx-signed 8 | test: true 9 | spec: 10 | replicas: 1 11 | selector: 12 | matchLabels: 13 | app: global-nginx-signed 14 | template: 15 | metadata: 16 | labels: 17 | app: global-nginx-signed 18 | spec: 19 | containers: 20 | - name: nginx 21 | image: icr.io/portieris-authn/nginx:signed 22 | ports: 23 | - containerPort: 80 24 | -------------------------------------------------------------------------------- /test/e2e/testdata/daemonset/global-nginx-unsigned.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: DaemonSet 3 | metadata: 4 | name: global-nginx-unsigned 5 | labels: 6 | app: global-nginx-unsigned 7 | test: true 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app: global-nginx-unsigned 13 | template: 14 | metadata: 15 | labels: 16 | app: global-nginx-unsigned 17 | spec: 18 | containers: 19 | - name: nginx 20 | image: icr.io/portieris-authn/nginx:unsigned 21 | ports: 22 | - containerPort: 80 23 | -------------------------------------------------------------------------------- /test/e2e/testdata/deployment/dockerhub-nginx-unsigned.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: dockerhub-nginx-unsigned 5 | labels: 6 | app: dockerhub-nginx-unsigned 7 | test: true 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app: dockerhub-nginx-unsigned 13 | template: 14 | metadata: 15 | labels: 16 | app: dockerhub-nginx-unsigned 17 | spec: 18 | containers: 19 | - name: nginx 20 | image: nginx:1.26.0 21 | ports: 22 | - containerPort: 80 23 | -------------------------------------------------------------------------------- /test/e2e/testdata/deployment/global-nginx-another.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: global-nginx-another 5 | labels: 6 | app: global-nginx-another 7 | test: true 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app: global-nginx-another 13 | template: 14 | metadata: 15 | labels: 16 | app: global-nginx-another 17 | spec: 18 | containers: 19 | - name: nginx 20 | image: icr.io/cise/nginx:another 21 | ports: 22 | - containerPort: 80 23 | -------------------------------------------------------------------------------- /test/e2e/testdata/deployment/global-nginx-multisigned.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: global-nginx-multisigned 5 | labels: 6 | app: global-nginx-multisigned 7 | test: true 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app: global-nginx-multisigned 13 | template: 14 | metadata: 15 | labels: 16 | app: global-nginx-multisigned 17 | spec: 18 | containers: 19 | - name: nginx 20 | image: de.icr.io/testing-authn/nginx:multisigned 21 | ports: 22 | - containerPort: 80 23 | -------------------------------------------------------------------------------- /test/e2e/testdata/deployment/global-nginx-remapped.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: global-nginx-remap 5 | labels: 6 | app: global-nginx-remap 7 | test: "true" 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app: global-nginx-remap 13 | template: 14 | metadata: 15 | labels: 16 | app: global-nginx-remap 17 | spec: 18 | containers: 19 | - name: nginx 20 | image: icr.io/cise/remap/nginx:simple 21 | ports: 22 | - containerPort: 80 23 | -------------------------------------------------------------------------------- /test/e2e/testdata/deployment/global-nginx-signed-free.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: global-nginx-simple 5 | labels: 6 | app: global-nginx-simple 7 | test: "true" 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app: global-nginx-simple 13 | template: 14 | metadata: 15 | labels: 16 | app: global-nginx-simple 17 | spec: 18 | containers: 19 | - name: nginx 20 | image: icr.io/cise/nginx:simple 21 | ports: 22 | - containerPort: 80 23 | -------------------------------------------------------------------------------- /test/e2e/testdata/deployment/global-nginx-signed-signed.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: global-nginx-signed-signed 5 | labels: 6 | app: global-nginx-signed-signed 7 | test: "true" 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app: global-nginx-signed-signed 13 | template: 14 | metadata: 15 | labels: 16 | app: global-nginx-signed-signed 17 | spec: 18 | containers: 19 | - name: nginx-signed 20 | image: de.icr.io/testing-authn/nginx:signed 21 | ports: 22 | - containerPort: 80 23 | - name: nginx-alsosigned 24 | image: de.icr.io/testing-authn/nginx:signed 25 | ports: 26 | - containerPort: 81 27 | command: ["/bin/sh"] 28 | args: ["-c", "sed -i 's/listen 80;/listen 81;/g' etc/nginx/conf.d/default.conf && \ 29 | nginx -g 'daemon off;'"] 30 | -------------------------------------------------------------------------------- /test/e2e/testdata/deployment/global-nginx-signed-unsigned.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: global-nginx-signed-unsigned 5 | labels: 6 | app: global-nginx-signed-unsigned 7 | test: true 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app: global-nginx-signed-unsigned 13 | template: 14 | metadata: 15 | labels: 16 | app: global-nginx-signed-unsigned 17 | spec: 18 | containers: 19 | - name: nginx-signed 20 | image: icr.io/cise/nginx:signed 21 | ports: 22 | - containerPort: 80 23 | - name: nginx-unsigned 24 | image: icr.io/cise/nginx:unsigned 25 | ports: 26 | - containerPort: 81 27 | command: ["/bin/sh"] 28 | args: ["-c", "sed -i 's/listen 80;/listen 81;/g' etc/nginx/conf.d/default.conf && \ 29 | nginx -g 'daemon off;'"] 30 | -------------------------------------------------------------------------------- /test/e2e/testdata/deployment/global-nginx-signed.yaml: -------------------------------------------------------------------------------- 1 | # simple signed 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: global-nginx-signed 6 | labels: 7 | app: global-nginx-signed 8 | test: true 9 | spec: 10 | replicas: 1 11 | selector: 12 | matchLabels: 13 | app: global-nginx-signed 14 | template: 15 | metadata: 16 | labels: 17 | app: global-nginx-signed 18 | spec: 19 | containers: 20 | - name: nginx 21 | image: icr.io/portieris-authn/nginx:signed 22 | ports: 23 | - containerPort: 80 24 | -------------------------------------------------------------------------------- /test/e2e/testdata/deployment/global-nginx-unsigned.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: global-nginx-unsigned 5 | labels: 6 | app: global-nginx-unsigned 7 | test: true 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app: global-nginx-unsigned 13 | template: 14 | metadata: 15 | labels: 16 | app: global-nginx-unsigned 17 | spec: 18 | containers: 19 | - name: nginx 20 | image: icr.io/portieris-authn/nginx:unsigned 21 | ports: 22 | - containerPort: 80 23 | -------------------------------------------------------------------------------- /test/e2e/testdata/deployment/global-signed-patch-to-unsigned.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: global-nginx-signed 5 | labels: 6 | app: global-nginx-signed 7 | test: true 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app: global-nginx-signed 13 | template: 14 | metadata: 15 | labels: 16 | app: global-nginx-signed 17 | spec: 18 | containers: 19 | - name: scratch 20 | image: icr.io/cise/nginx:unsigned 21 | ports: 22 | - containerPort: 80 23 | -------------------------------------------------------------------------------- /test/e2e/testdata/deployment/vulnerability-account-exemption.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: nginx-config-issue 5 | labels: 6 | app: nginx-config-issue 7 | test: "true" 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app: nginx-config-issue 13 | template: 14 | metadata: 15 | labels: 16 | app: nginx-config-issue 17 | spec: 18 | containers: 19 | - name: nginx-config-issue 20 | image: de.icr.io/testing-free/nginx:config 21 | ports: 22 | - containerPort: 80 23 | -------------------------------------------------------------------------------- /test/e2e/testdata/deployment/vulnerability-allow.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: simplego 5 | labels: 6 | app: simplego 7 | test: "true" 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app: simplego 13 | template: 14 | metadata: 15 | labels: 16 | app: simplego 17 | spec: 18 | containers: 19 | - name: goserv 20 | image: de.icr.io/testing-authn/simplegoserver:unsupported 21 | ports: 22 | - containerPort: 80 23 | -------------------------------------------------------------------------------- /test/e2e/testdata/deployment/vulnerability-deny-vulnerable-signed.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: nginx-signed-vulnerable 5 | labels: 6 | app: nginx-signed-vulnerable 7 | test: "true" 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app: nginx-signed-vulnerable 13 | template: 14 | metadata: 15 | labels: 16 | app: nginx-signed-vulnerable 17 | spec: 18 | containers: 19 | - name: nginx-vulnerable-and-signed 20 | image: de.icr.io/testing-authn/nginx:simple 21 | ports: 22 | - containerPort: 80 23 | -------------------------------------------------------------------------------- /test/e2e/testdata/deployment/vulnerability-deny.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: nginx-signed-vulnerable 5 | labels: 6 | app: nginx-signed-vulnerable 7 | test: "true" 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app: nginx-signed-vulnerable 13 | template: 14 | metadata: 15 | labels: 16 | app: nginx-signed-vulnerable 17 | spec: 18 | containers: 19 | - name: nginx-vulnerable 20 | image: de.icr.io/testing-authn/nginx:signed 21 | ports: 22 | - containerPort: 80 23 | -------------------------------------------------------------------------------- /test/e2e/testdata/imagepolicy/allow-all.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: portieris.cloud.ibm.com/v1 2 | kind: ImagePolicy 3 | metadata: 4 | name: allow-all 5 | spec: 6 | repositories: 7 | - name: "*" 8 | policy: {} 9 | -------------------------------------------------------------------------------- /test/e2e/testdata/imagepolicy/allow-signed-custom.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: portieris.cloud.ibm.com/v1 2 | kind: ImagePolicy 3 | metadata: 4 | name: allow-signed 5 | spec: 6 | repositories: 7 | - name: "de.icr.io/testing-authn/nginx" 8 | policy: 9 | trust: 10 | enabled: true 11 | trustServer: "https://notary.de.icr.io" 12 | -------------------------------------------------------------------------------- /test/e2e/testdata/imagepolicy/allow-signed-dockerhub.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: portieris.cloud.ibm.com/v1 2 | kind: ImagePolicy 3 | metadata: 4 | name: allow-signed 5 | spec: 6 | repositories: 7 | - name: "docker.io/nginx" 8 | policy: 9 | trust: 10 | enabled: true 11 | -------------------------------------------------------------------------------- /test/e2e/testdata/imagepolicy/allow-signed.yaml: -------------------------------------------------------------------------------- 1 | # allow simple sign from icr.io/portieris-authn 2 | apiVersion: portieris.cloud.ibm.com/v1 3 | kind: ImagePolicy 4 | metadata: 5 | name: allow-signed 6 | spec: 7 | repositories: 8 | - name: "icr.io/portieris-authn/nginx" 9 | policy: 10 | simple: 11 | requirements: 12 | - type: "signedBy" 13 | keySecret: simple1pubkey -------------------------------------------------------------------------------- /test/e2e/testdata/imagepolicy/allow-unsigned-embedded-trailing-wildcard.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: portieris.cloud.ibm.com/v1 2 | kind: ImagePolicy 3 | metadata: 4 | name: allow-unsigned-embedded-trailing-wildcard 5 | spec: 6 | repositories: 7 | - name: "icr.io/*/*" 8 | policy: 9 | trust: 10 | enabled: false 11 | -------------------------------------------------------------------------------- /test/e2e/testdata/imagepolicy/allow-unsigned-embedded-wildcard.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: portieris.cloud.ibm.com/v1 2 | kind: ImagePolicy 3 | metadata: 4 | name: allow-unsigned-embedded-wildcard 5 | spec: 6 | repositories: 7 | - name: "icr.io/*/nginx" 8 | policy: 9 | trust: 10 | enabled: false 11 | -------------------------------------------------------------------------------- /test/e2e/testdata/imagepolicy/allow-unsigned-trailing-wildcard.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: sportieris.cloud.ibm.com/v1 2 | kind: ImagePolicy 3 | metadata: 4 | name: allow-unsigned-trailing-wildcard 5 | spec: 6 | repositories: 7 | - name: "icr.io/portieris-authn/*" 8 | policy: 9 | trust: 10 | enabled: false 11 | -------------------------------------------------------------------------------- /test/e2e/testdata/imagepolicy/pinned-multi.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: portieris.cloud.ibm.com/v1 2 | kind: ImagePolicy 3 | metadata: 4 | name: pinned-multi 5 | spec: 6 | repositories: 7 | - name: "de.icr.io/testing-authn/nginx" 8 | policy: 9 | trust: 10 | enabled: true 11 | trustServer: "https://notary.de.icr.io" 12 | signerSecrets: 13 | - name: signer1pubkey 14 | - name: signer2pubkey 15 | -------------------------------------------------------------------------------- /test/e2e/testdata/imagepolicy/pinned-signer1.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: portieris.cloud.ibm.com/v1 2 | kind: ImagePolicy 3 | metadata: 4 | name: pinned-signer1 5 | spec: 6 | repositories: 7 | - name: "de.icr.io/testing-authn/nginx" 8 | policy: 9 | trust: 10 | enabled: true 11 | trustServer: "https://notary.de.icr.io" 12 | signerSecrets: 13 | - name: signer1pubkey 14 | -------------------------------------------------------------------------------- /test/e2e/testdata/imagepolicy/pinned-signer2.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: portieris.cloud.ibm.com/v1 2 | kind: ImagePolicy 3 | metadata: 4 | name: pinned-signer2 5 | spec: 6 | repositories: 7 | - name: "de.icr.io/testing-authn/nginx" 8 | policy: 9 | trust: 10 | enabled: true 11 | trustServer: "https://notary.de.icr.io" 12 | signerSecrets: 13 | - name: signer2pubkey 14 | -------------------------------------------------------------------------------- /test/e2e/testdata/imagepolicy/simple-accept-anything.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: portieris.cloud.ibm.com/v1 2 | kind: ImagePolicy 3 | metadata: 4 | name: simple-accept-anything 5 | spec: 6 | repositories: 7 | - name: "*" 8 | policy: 9 | simple: 10 | requirements: 11 | - type: "insecureAcceptAnything" 12 | -------------------------------------------------------------------------------- /test/e2e/testdata/imagepolicy/simple-remap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: portieris.cloud.ibm.com/v1 2 | kind: ImagePolicy 3 | metadata: 4 | name: simple-remap 5 | spec: 6 | repositories: 7 | - name: "icr.io/cise/*" 8 | policy: 9 | simple: 10 | requirements: 11 | - type: "signedBy" 12 | keySecret: simple1pubkey 13 | signedIdentity: 14 | type: "remapIdentity" 15 | prefix: "icr.io/cise/remap" 16 | signedPrefix: "icr.io/cise" 17 | -------------------------------------------------------------------------------- /test/e2e/testdata/imagepolicy/simple-signedby1-keysecret-namespace-override.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: portieris.cloud.ibm.com/v1 2 | kind: ImagePolicy 3 | metadata: 4 | name: simple-signedby1 5 | spec: 6 | repositories: 7 | - name: "icr.io/cise/*" 8 | policy: 9 | simpleStore: 10 | url: "https://foo.com/x" 11 | auth: storesecret 12 | simple: 13 | requirements: 14 | - type: "signedBy" 15 | keySecret: simple1pubkey 16 | keySecretNamespace: secretnamespace 17 | -------------------------------------------------------------------------------- /test/e2e/testdata/imagepolicy/simple-signedby1.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: portieris.cloud.ibm.com/v1 2 | kind: ImagePolicy 3 | metadata: 4 | name: simple-signedby1 5 | spec: 6 | repositories: 7 | - name: "icr.io/cise/*" 8 | policy: 9 | simpleStore: 10 | url: "https://foo.com/x" 11 | auth: storesecret 12 | simple: 13 | requirements: 14 | - type: "signedBy" 15 | keySecret: simple1pubkey 16 | -------------------------------------------------------------------------------- /test/e2e/testdata/imagepolicy/simple-signedby2-mutate.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: portieris.cloud.ibm.com/v1 2 | kind: ImagePolicy 3 | metadata: 4 | name: simple-signedby2-mutate 5 | spec: 6 | repositories: 7 | - name: "icr.io/cise/*" 8 | policy: 9 | mutateImage: true 10 | simple: 11 | requirements: 12 | - type: "signedBy" 13 | keySecret: simple1pubkey 14 | - type: "signedBy" 15 | keySecret: simple2pubkey -------------------------------------------------------------------------------- /test/e2e/testdata/imagepolicy/simple-signedby2-nomutate.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: portieris.cloud.ibm.com/v1 2 | kind: ImagePolicy 3 | metadata: 4 | name: simple-signedby2-nomutate 5 | spec: 6 | repositories: 7 | - name: "icr.io/cise/*" 8 | policy: 9 | mutateImage: false 10 | simple: 11 | requirements: 12 | - type: "signedBy" 13 | keySecret: simple1pubkey 14 | - type: "signedBy" 15 | keySecret: simple2pubkey -------------------------------------------------------------------------------- /test/e2e/testdata/imagepolicy/simple-signedby2.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: portieris.cloud.ibm.com/v1 2 | kind: ImagePolicy 3 | metadata: 4 | name: simple-signedby2 5 | spec: 6 | repositories: 7 | - name: "icr.io/cise/*" 8 | policy: 9 | simple: 10 | requirements: 11 | - type: "signedBy" 12 | keySecret: simple1pubkey 13 | - type: "signedBy" 14 | keySecret: simple2pubkey 15 | -------------------------------------------------------------------------------- /test/e2e/testdata/imagepolicy/vulnerability-enabled-with-simple.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: portieris.cloud.ibm.com/v1 2 | kind: ImagePolicy 3 | metadata: 4 | name: vulnerability-enabled 5 | spec: 6 | repositories: 7 | - name: "de.icr.io/testing-authn/*" 8 | policy: 9 | simple: 10 | requirements: 11 | - type: "signedBy" 12 | keySecret: simple1pubkey 13 | vulnerability: 14 | ICCRVA: 15 | enabled: true 16 | -------------------------------------------------------------------------------- /test/e2e/testdata/imagepolicy/vulnerability-enabled.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: portieris.cloud.ibm.com/v1 2 | kind: ImagePolicy 3 | metadata: 4 | name: vulnerability-enabled 5 | spec: 6 | repositories: 7 | - name: "de.icr.io/testing-authn/*" 8 | policy: 9 | vulnerability: 10 | ICCRVA: 11 | enabled: true 12 | -------------------------------------------------------------------------------- /test/e2e/testdata/imagepolicy/vulnerability-with-account.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: portieris.cloud.ibm.com/v1 2 | kind: ImagePolicy 3 | metadata: 4 | name: vulnerability-enabled 5 | spec: 6 | repositories: 7 | - name: "de.icr.io/testing-free/*" 8 | policy: 9 | vulnerability: 10 | ICCRVA: 11 | enabled: true 12 | account: "ENV" 13 | -------------------------------------------------------------------------------- /test/e2e/testdata/job/global-nginx-signed.yaml: -------------------------------------------------------------------------------- 1 | # simple signed 2 | apiVersion: batch/v1 3 | kind: Job 4 | metadata: 5 | name: global-nginx-signed 6 | labels: 7 | app: global-nginx-signed 8 | test: true 9 | spec: 10 | template: 11 | spec: 12 | containers: 13 | - name: nginx 14 | image: icr.io/portieris-authn/nginx:signed 15 | ports: 16 | - containerPort: 80 17 | restartPolicy: Never 18 | -------------------------------------------------------------------------------- /test/e2e/testdata/job/global-nginx-unsigned.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: batch/v1 2 | kind: Job 3 | metadata: 4 | name: global-nginx-unsigned 5 | labels: 6 | app: global-nginx-unsigned 7 | test: true 8 | spec: 9 | template: 10 | spec: 11 | containers: 12 | - name: nginx 13 | image: icr.io/portieris-authn/nginx:unsigned 14 | ports: 15 | - containerPort: 80 16 | restartPolicy: Never 17 | -------------------------------------------------------------------------------- /test/e2e/testdata/pod/global-nginx-signed.yaml: -------------------------------------------------------------------------------- 1 | # simple signed 2 | apiVersion: v1 3 | kind: Pod 4 | metadata: 5 | name: global-nginx-signed 6 | labels: 7 | app: global-nginx-signed 8 | test: "true" 9 | spec: 10 | containers: 11 | - name: nginx 12 | image: icr.io/portieris-authn/nginx:signed 13 | ports: 14 | - containerPort: 80 15 | -------------------------------------------------------------------------------- /test/e2e/testdata/pod/global-nginx-unsigned.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: global-nginx-unsigned 5 | labels: 6 | app: global-nginx-unsigned 7 | test: "true" 8 | spec: 9 | containers: 10 | - name: nginx 11 | image: icr.io/portieris-authn/nginx:unsigned 12 | ports: 13 | - containerPort: 80 -------------------------------------------------------------------------------- /test/e2e/testdata/replicaset/global-nginx-signed.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: ReplicaSet 3 | metadata: 4 | name: global-nginx-signed 5 | labels: 6 | app: global-nginx-signed 7 | test: true 8 | spec: 9 | selector: 10 | matchLabels: 11 | app: global-nginx-signed 12 | template: 13 | metadata: 14 | labels: 15 | app: global-nginx-signed 16 | spec: 17 | containers: 18 | - name: nginx 19 | image: icr.io/portieris-authn/nginx:signed 20 | ports: 21 | - containerPort: 80 -------------------------------------------------------------------------------- /test/e2e/testdata/replicaset/global-nginx-unsigned.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: ReplicaSet 3 | metadata: 4 | name: global-nginx-unsigned 5 | labels: 6 | app: global-nginx-unsigned 7 | test: true 8 | spec: 9 | selector: 10 | matchLabels: 11 | app: global-nginx-unsigned 12 | template: 13 | metadata: 14 | labels: 15 | app: global-nginx-unsigned 16 | spec: 17 | containers: 18 | - name: nginx 19 | image: icr.io/portieris-authn/nginx:unsigned 20 | ports: 21 | - containerPort: 80 22 | -------------------------------------------------------------------------------- /test/e2e/testdata/replicationcontroller/global-nginx-signed.yaml: -------------------------------------------------------------------------------- 1 | # simple signed 2 | apiVersion: v1 3 | kind: ReplicationController 4 | metadata: 5 | name: global-nginx-signed 6 | labels: 7 | app: global-nginx-signed 8 | test: "true" 9 | spec: 10 | template: 11 | metadata: 12 | labels: 13 | name: global-nginx-signed 14 | spec: 15 | containers: 16 | - name: nginx 17 | image: icr.io/portieris-authn/nginx:signed 18 | ports: 19 | - containerPort: 80 20 | -------------------------------------------------------------------------------- /test/e2e/testdata/replicationcontroller/global-nginx-unsigned.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ReplicationController 3 | metadata: 4 | name: global-nginx-unsigned 5 | labels: 6 | app: global-nginx-unsigned 7 | test: "true" 8 | spec: 9 | template: 10 | metadata: 11 | labels: 12 | name: global-nginx-unsigned 13 | spec: 14 | containers: 15 | - name: nginx 16 | image: icr.io/portieris-authn/nginx:unsigned 17 | ports: 18 | - containerPort: 80 19 | -------------------------------------------------------------------------------- /test/e2e/testdata/secret/signer1.pubkey.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | type: Opaque 4 | metadata: 5 | name: signer1pubkey 6 | data: 7 | name: c2lnbmVyMQ== 8 | publicKey: LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0Kcm9sZTogc2lnbmVyMQoKTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFSUFnM3hGeXdlMllRMlF6bnV3S2c4RURURTRhagpzQjl6Yld4SmVFcDN4QlRuTXYydjF2SEZ3N2ZQYzByZ1VwTWhyckdhQU1hQm55TjZVQ1crZVhjQ3dnPT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg== 9 | -------------------------------------------------------------------------------- /test/e2e/testdata/secret/signer2.pubkey.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | type: Opaque 4 | metadata: 5 | name: signer2pubkey 6 | data: 7 | name: c2lnbmVyMg== 8 | publicKey: LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0Kcm9sZTogc2lnbmVyMgoKTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFaC82dDQzUzhzNEJHbEJlVEJReG1vRmRDTHVuSQo4bXRqVVJQb0RZcjFWNkw4bnZMYk15V0JsY2wweHpwOHo0cXlvNHYzSVltdzZRL0RsbEVuSkU4a3V3PT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg== 9 | -------------------------------------------------------------------------------- /test/e2e/testdata/secret/simple1pubkey.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | type: Opaque 4 | metadata: 5 | name: simple1pubkey 6 | stringData: 7 | key: | 8 | -----BEGIN PGP PUBLIC KEY BLOCK----- 9 | 10 | mQENBF15FJUBCAC+RDRL14lFAeVUAQrsg7XU3tLEb6Goy+XADZL1VLOgjDNqbkM8 11 | UnHRlGAVcMkui/vaiF/PHQchIc64vbQFjHsswxuNiRpL1n72k3dq9fQkdE5uMFtg 12 | m/LYlqFJDOhdFWarUUvBW1rTAwZAxWQSsZGGzTasSzA2JtiAR51qAMF3JZxV6RAR 13 | vIAf4XqdVTG/LhbA15GTDx4zGI30hb29pVV6d6nV+qEvXP4QTOQ27dBv8ZN1d8rD 14 | SQI7fhb7xoXt6xqsSjFl+rgCCyoRbCCWpdQIhcBLqK4O8MEYp2M+D5YpO8WV4OM9 15 | EDx9YhFpsNaOirzfd1ZQZ+vUpT7qFq2kqen1ABEBAAG0KFN0dWFydCBIYXl0b24g 16 | PHN0dWFydC5oYXl0b25AdWsuaWJtLmNvbT6JAVQEEwEIAD4WIQR3TcmcAGUBN1Ic 17 | i7Pxx2Awu2yqjQUCXXkUlQIbAwUJA8JnAAULCQgHAgYVCgkICwIEFgIDAQIeAQIX 18 | gAAKCRDxx2Awu2yqjbtkCACaUuWxKuuw1+kDKy06Ir1/+mrPrNHiPndmmOxvVrhT 19 | JqmukXHSq3HgXxJWCnU+ubhmnKV7StK8pG8bNSFVtTxVKfcedQGZlvQY6/avGfd7 20 | BysKpFQl9QjAwojcirVFmOzA/bfVY1lGUivnxOUwzPsznngl+fsG3s9VEYYnry8D 21 | oeewR6Xy4d+EB/phTK61Oh+gB7Gic3wnf5HJMKWUl4GchyzPpxRi2az8tfBS9tkH 22 | NaqtzIb5QV9mZ2/LQ/opXvE56yyM9jRaQqKdeO6MtQus2AI8w2NYl3PNFK/Ncblc 23 | JKEMlNJ6d+j/stk/mCNmRvebutiDYuTKCjqmW1lYleP9uQENBF15FJUBCACckqwm 24 | DBKPp93nXSyJzH8Di9cC7cL58Q6pGjcwG4GhanfbDxR0eDem/l2Ccn3lVoBdSM8P 25 | 5SGRCbQdgUNfreHofjp6idcFg/rkjc2Q5BS+fQ0HDfFuLMnS3eKuwFbRSHtNKDP/ 26 | fKiIgKzx4ra55S7lgVX8Skh11acFHkuH+9xpeV+bv84F28TCZ+pL+G2XYRqYKNvA 27 | nGB5PmCfUwZJlgJEu29F7sYiplYD5nIWBSz0ZwzWM+wSGCdntgxYuw+7c+3vfOws 28 | gAOpgqXXNHwpRSd1xazbTpu8Kz1nWeZ8w8aPmYKuo9+ucMbpzYpqmyiXb1DiHbxO 29 | VsE3ZM6kBIyl7H5HABEBAAGJATwEGAEIACYWIQR3TcmcAGUBN1Ici7Pxx2Awu2yq 30 | jQUCXXkUlQIbDAUJA8JnAAAKCRDxx2Awu2yqjQ4xCACRYNG/6JpKuOjsU/LSpw8G 31 | rBNjFMlzNdiPOdHiW/gglBbMJB3LJJrM4TvMcFsqmuKUh1j7/gO9GUhm3VIRxZXx 32 | mble0sEh5n6Tpz0HoZb2ndvi+tqbMm1ufDP9pbIXOZzdksywrAX3283vjDUTlDog 33 | 7qYBzQEG6TK68RGDKGobDtBIoR9S/enHoAkrWONKJ9uyJw2cIpx72MPXiMqP6vnL 34 | Exdgp01NoEQx1UPfy/Y9gJ5aGaUUBDG7i6twpeTo9XFyJihrU5tFfrzT6iuGggxF 35 | fJoCgxVAKzXJnGTulcClquAOmMCFKqxbkOTIUy0uATSGF4pIvGu0Edi0GzvfCKST 36 | =Q8Ys 37 | -----END PGP PUBLIC KEY BLOCK----- 38 | 39 | -------------------------------------------------------------------------------- /test/e2e/testdata/secret/simple2pubkey.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | type: Opaque 4 | metadata: 5 | name: simple2pubkey 6 | stringData: 7 | key: | 8 | -----BEGIN PGP PUBLIC KEY BLOCK----- 9 | 10 | mQENBF53v4cBCADMpnM4Bs/H7AVPTaZM21bFiC1MyveUhKLhDndxaYqcWFp80QVs 11 | CWDSXaMwyTpsa6Iy/vLGMDrK6DR6neb091U42C9UR+o7jFx8kFQ0jVIdF2VDP5eW 12 | mNm/rz6HzRzLsjAmYtUBcgGu0DiQs2L9b379U+6NYfNFNlaxH2Cv2LHB1HknRF+4 13 | 4FoQuXsRZRNqFwojd1Wg1wmtg/uktVMbq6MPHg0pZ7m7W33rFUE8WLliji8twov/ 14 | z0LnJnjjIsOaX7inlSdko+IgMcLsp/bLGk3e/9K/A4KfLfG6Unwcg0xSIQR3IORA 15 | giaiu41oIykgsBRx21wY9rjTFaEQde32qSDDABEBAAG0LUFsY2hlbXkgRGV2IDxB 16 | bGNoZW15LlJlZ2lzdHJ5LkRldkB1ay5pYm0uY29tPokBVAQTAQgAPhYhBO/8/JTN 17 | UJDoHC62rQhoRAs38nxjBQJed7+HAhsDBQkDwmcABQsJCAcCBhUKCQgLAgQWAgMB 18 | Ah4BAheAAAoJEAhoRAs38nxjkb0H/2iR5HSpXgBOarhDqytRiCnDxaJSChs6X4z9 19 | RQKdMeNejM2FFtbuBSGkLIrtktbnTeN5FxtZRRfHEB8pGxbN8BLEkuUlGInmtAhf 20 | /24ZC+BDYtJOGFqd74CgG082ff+mrk2f/cLK0cT8H+JsvsULHvONe58hRoAiTP7c 21 | ceNCO0K4vO4f5QaFZSrcLTd0mGKEEhTCipxXipArcANktegxBo8be3GY2sXYMGbu 22 | twRFs6/B/cUs5fwpADp12M0wLcQykWRa0Bs4Gr11qs2l2JVGoeaO9a871j+G/vAY 23 | 1zNQG6zkF89/NP4KWzK1lD7BjW7CVzqcAnohudtPxfJPKDHYQtS5AQ0EXne/hwEI 24 | ALnoYCsvdTiHaYB4NXIBBPTysUq4erW273ZnCARGm4pq/Cyuk6rbGY0wjjbDI2au 25 | IcO694ZJEK+++E27CjJI60V+e+J+hkBhJSuDg4qZK4yjsLToqhq3uIVA1CBvSQ/F 26 | pcu01b3Qgev6dLryeR+VRFzrCGzTmRkBPdFSW1D5y/FWKLPLer1YEn/wh5vW4mg6 27 | nmNwRLVzcqGOskvQeLbL4aBm+4scCcXKq7fkoD0jc1y3ELpMuwJg2kJUhmHhUCaN 28 | ZKLFCSgZhQoK/WM0zG1ch4XIkANTC14yuufg1C0INRnlEoNmfaIqeNiGuA0zPq3K 29 | JD+nZ/5wHgYVo3pjDTgrzn0AEQEAAYkBPAQYAQgAJhYhBO/8/JTNUJDoHC62rQho 30 | RAs38nxjBQJed7+HAhsMBQkDwmcAAAoJEAhoRAs38nxjm0wIALIz+o3MPXXaXwz3 31 | qlu0mrLGTyRwmDvIJGYz84J+cgPuccv2gV9bZeNL7YkWAys+vH9zQinfyahjEd6i 32 | rCC2dxXlpPf60oQEzRKPPW2rrqYYS7YHwJYSf+xJwyo9Lo4ibPwhWe+9fK/MoDTU 33 | aGKpzr2MV9APQ554JuSS3NO06MN/p49sQFteDTO16K6gLQyU71c5w1zwwQhphC2V 34 | +jrJcD3ZXkqFOT32raXLgeGKkgC13ihzSdXMXxqkxDRpjA9Xhw6PRGuC28LT/LqQ 35 | PAd4+7MLZ4VTgQwD0LNRTfvWs6yvQO78MKw1n9Yn6iA9R0xdS7Ez5UyX9abCSQBy 36 | f5Ygwng= 37 | =Pdub 38 | -----END PGP PUBLIC KEY BLOCK-----" 39 | -------------------------------------------------------------------------------- /test/e2e/testdata/statefulset/global-nginx-signed.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: StatefulSet 3 | metadata: 4 | name: global-nginx-signed 5 | labels: 6 | app: global-nginx-signed 7 | test: true 8 | spec: 9 | selector: 10 | matchLabels: 11 | name: global-nginx-signed 12 | template: 13 | metadata: 14 | labels: 15 | name: global-nginx-signed 16 | spec: 17 | containers: 18 | - name: nginx 19 | image: icr.io/portieris-authn/nginx:signed 20 | ports: 21 | - containerPort: 80 22 | -------------------------------------------------------------------------------- /test/e2e/testdata/statefulset/global-nginx-unsigned.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: StatefulSet 3 | metadata: 4 | name: global-nginx-unsigned 5 | labels: 6 | app: global-nginx-unsigned 7 | test: true 8 | spec: 9 | selector: 10 | matchLabels: 11 | name: global-nginx-unsigned 12 | template: 13 | metadata: 14 | labels: 15 | name: global-nginx-unsigned 16 | spec: 17 | containers: 18 | - name: nginx 19 | image: icr.io/portieris-authn/nginx:unsigned 20 | ports: 21 | - containerPort: 80 22 | -------------------------------------------------------------------------------- /test/e2e/utils/clusterimagepolicy.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018, 2022 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package utils 16 | 17 | import ( 18 | "testing" 19 | 20 | policyv1 "github.com/IBM/portieris/pkg/apis/portieris.cloud.ibm.com/v1" 21 | "github.com/IBM/portieris/test/framework" 22 | corev1 "k8s.io/api/core/v1" 23 | ) 24 | 25 | func CheckIfTesting(t *testing.T, boolToCheck bool) { 26 | if !boolToCheck { 27 | t.Skip() 28 | } 29 | } 30 | 31 | // DeleteThenReturnClusterImagePolicy is used for the temporary deletion of a cluster image policy for a given test. 32 | // The returned ClusterImagePolicy must be used to re-create the cluster image policy after the test is complete by using a defer. 33 | func DeleteThenReturnClusterImagePolicy(t *testing.T, fw *framework.Framework, clusterImagePolicy string) *policyv1.ClusterImagePolicy { 34 | defaultClusterPolicy, err := fw.GetClusterImagePolicy(clusterImagePolicy) 35 | if err != nil { 36 | return nil 37 | } 38 | if err := fw.DeleteClusterImagePolicy(clusterImagePolicy); err != nil { 39 | t.Errorf("error deleting ClusterImagePolicy %q: %v", clusterImagePolicy, err) 40 | } 41 | return defaultClusterPolicy 42 | } 43 | 44 | func CreateClusterImagePolicyAndNamespace(t *testing.T, fw *framework.Framework, manifestPath string) (*policyv1.ClusterImagePolicy, *corev1.Namespace) { 45 | ns := framework.MakeTestUUID() 46 | clusterImagePolicy, err := fw.LoadClusterImagePolicyManifest(manifestPath) 47 | if err != nil { 48 | t.Errorf("error loading %q ClusterImagePolicy manifest: %v", clusterImagePolicy.Name, err) 49 | } 50 | namespace, err := fw.CreateNamespaceWithIPS(ns) 51 | if err != nil { 52 | t.Errorf("error creating %q namespace: %v", ns, err) 53 | } 54 | if err := fw.CreateClusterImagePolicy(clusterImagePolicy); err != nil { 55 | t.Errorf("error creating %q ClusterImagePolicy: %v", clusterImagePolicy.Name, err) 56 | } 57 | return clusterImagePolicy, namespace 58 | } 59 | 60 | func CleanUpClusterImagePolicyTest(t *testing.T, fw *framework.Framework, clusterPolicy, namespace string) { 61 | if err := fw.DeleteNamespace(namespace); err != nil { 62 | t.Logf("failed to delete namespace %q: %v", namespace, err) 63 | } 64 | if err := fw.DeleteClusterImagePolicy(clusterPolicy); err != nil { 65 | t.Logf("failed to delete ClusterImagePolicy %q: %v", clusterPolicy, err) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /test/e2e/utils/cronjob.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018, 2023 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package utils 16 | 17 | import ( 18 | "testing" 19 | "time" 20 | 21 | "github.com/IBM/portieris/test/framework" 22 | "github.com/stretchr/testify/assert" 23 | batchv1 "k8s.io/api/batch/v1" 24 | ) 25 | 26 | func buildCronJob(t *testing.T, fw *framework.Framework, manifestLocation, namespace string, expectCreateFail bool) *batchv1.CronJob { 27 | manifest, err := fw.LoadCronJobManifest(manifestLocation) 28 | if err != nil { 29 | t.Fatalf("Error loading manifest: %v", err) 30 | } 31 | if manifest == nil { 32 | t.Fatalf("Error loading manifest: manifest is nil") 33 | } 34 | if err = fw.CreateCronJob(namespace, manifest); err != nil { 35 | if !expectCreateFail { 36 | t.Fatalf("Failed to create cronjob on success path: %s", err) 37 | } else { 38 | return nil 39 | } 40 | } 41 | 42 | fw.WaitForCronJob(manifest.Name, namespace, time.Second*61) 43 | cronjob, err := fw.GetCronJob(manifest.Name, namespace) 44 | if err != nil { 45 | t.Fatalf("Error getting %q cronjob in %v: %v", manifest.Name, namespace, err) 46 | } 47 | return cronjob 48 | } 49 | 50 | // TestCronJobRunnable tests whether a manifest is deployable to the specified namespace. 51 | func TestCronJobRunnable(t *testing.T, fw *framework.Framework, manifestLocation, namespace string) { 52 | cronjob := buildCronJob(t, fw, manifestLocation, namespace, false) 53 | defer fw.DeleteCronJob(cronjob.Name, cronjob.Namespace) 54 | if !assert.NotZero(t, len(cronjob.Status.Active), "CronJob failed ") { 55 | DumpEvents(t, fw, namespace) 56 | DumpPolicies(t, fw, namespace) 57 | } 58 | 59 | } 60 | 61 | // TestCronJobNotRunnable tests whether a manifest is deployable to the specified namespace. 62 | func TestCronJobNotRunnable(t *testing.T, fw *framework.Framework, manifestLocation, namespace string) { 63 | cronjob := buildCronJob(t, fw, manifestLocation, namespace, true) 64 | if cronjob != nil { 65 | if !assert.Zero(t, len(cronjob.Status.Active), "CronJob Running ") { 66 | DumpEvents(t, fw, namespace) 67 | DumpPolicies(t, fw, namespace) 68 | 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /test/e2e/utils/daemonset.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018, 2021 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package utils 16 | 17 | import ( 18 | "testing" 19 | "time" 20 | 21 | "github.com/IBM/portieris/test/framework" 22 | "github.com/stretchr/testify/assert" 23 | appsv1 "k8s.io/api/apps/v1" 24 | ) 25 | 26 | func buildDaemonSet(t *testing.T, fw *framework.Framework, manifestLocation, namespace string, expectCreateFail bool) *appsv1.DaemonSet { 27 | manifest, err := fw.LoadDaemonSetManifest(manifestLocation) 28 | if err != nil { 29 | t.Fatalf("Error loading manifest: %v", err) 30 | } 31 | if manifest == nil { 32 | t.Fatalf("Error loading manifest: manifest is nil") 33 | } 34 | if err = fw.CreateDaemonSet(namespace, manifest); err != nil { 35 | if !expectCreateFail { 36 | t.Fatalf("Error creating %q daemonsets in %v: %v", manifest.Name, namespace, err) 37 | } else { 38 | return nil 39 | } 40 | } 41 | fw.WaitForDaemonSetPods(manifest.Name, namespace, time.Minute) 42 | daemonset, err := fw.GetDaemonSets(manifest.Name, namespace) 43 | if err != nil { 44 | t.Fatalf("Error getting %q daemonsets in %v: %v", manifest.Name, namespace, err) 45 | } 46 | return daemonset 47 | } 48 | 49 | // TestDaemonSetRunnable tests whether a manifest is deployable to the specified namespace. 50 | func TestDaemonSetRunnable(t *testing.T, fw *framework.Framework, manifestLocation, namespace string) { 51 | daemonset := buildDaemonSet(t, fw, manifestLocation, namespace, false) 52 | defer fw.DeleteDaemonSet(daemonset.Name, daemonset.Namespace) 53 | if !assert.Equal(t, daemonset.Status.DesiredNumberScheduled, daemonset.Status.NumberReady, "DaemonSet failed: available replicas did not match expected replicas") { 54 | DumpEvents(t, fw, namespace) 55 | DumpPolicies(t, fw, namespace) 56 | } 57 | } 58 | 59 | // TestDaemonSetNotRunnable tests whether a manifest is deployable to the specified namespace. 60 | func TestDaemonSetNotRunnable(t *testing.T, fw *framework.Framework, manifestLocation, namespace string) { 61 | daemonset := buildDaemonSet(t, fw, manifestLocation, namespace, true) 62 | if daemonset != nil { 63 | defer fw.DeleteDaemonSet(daemonset.Name, daemonset.Namespace) 64 | DumpEvents(t, fw, namespace) 65 | DumpPolicies(t, fw, namespace) 66 | t.Errorf("Expected daemonset creation to fail") 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /test/e2e/utils/debug.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package utils 16 | 17 | import ( 18 | "io/ioutil" 19 | "testing" 20 | 21 | "github.com/IBM/portieris/test/framework" 22 | ) 23 | 24 | func DumpEvents(t *testing.T, fw *framework.Framework, namespace string) { 25 | reader := fw.DumpEvents(namespace) 26 | bytes, _ := ioutil.ReadAll(reader) 27 | t.Errorf("%s\n", bytes) 28 | } 29 | 30 | func DumpPolicies(t *testing.T, fw *framework.Framework, namespace string) { 31 | reader := fw.DumpPolicies(namespace) 32 | bytes, _ := ioutil.ReadAll(reader) 33 | t.Errorf("%s\n", bytes) 34 | } 35 | -------------------------------------------------------------------------------- /test/e2e/utils/job.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018, 2021 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package utils 16 | 17 | import ( 18 | "testing" 19 | "time" 20 | 21 | "github.com/IBM/portieris/test/framework" 22 | "github.com/stretchr/testify/assert" 23 | batchv1 "k8s.io/api/batch/v1" 24 | ) 25 | 26 | func buildJob(t *testing.T, fw *framework.Framework, manifestLocation, namespace string, expectCreateFail bool) *batchv1.Job { 27 | manifest, err := fw.LoadJobManifest(manifestLocation) 28 | if err != nil { 29 | t.Fatalf("Error loading manifest: %v", err) 30 | } 31 | if manifest == nil { 32 | t.Fatalf("Error loading manifest: manifest is nil") 33 | } 34 | if err = fw.CreateJob(namespace, manifest); err != nil { 35 | if !expectCreateFail { 36 | t.Fatalf("Failed to create job on success path: %s", err) 37 | } else { 38 | return nil 39 | } 40 | } 41 | 42 | fw.WaitForJob(manifest.Name, namespace, time.Second*30) 43 | job, err := fw.GetJob(manifest.Name, namespace) 44 | if err != nil { 45 | t.Fatalf("Error getting %q job in %v: %v", manifest.Name, namespace, err) 46 | } 47 | return job 48 | } 49 | 50 | // TestJobRunnable tests whether a manifest is deployable to the specified namespace. 51 | func TestJobRunnable(t *testing.T, fw *framework.Framework, manifestLocation, namespace string) { 52 | job := buildJob(t, fw, manifestLocation, namespace, false) 53 | defer fw.DeleteJob(job.Name, job.Namespace) 54 | if !assert.Zero(t, job.Status.Failed, "Job failed ") { 55 | DumpEvents(t, fw, namespace) 56 | DumpPolicies(t, fw, namespace) 57 | } 58 | 59 | } 60 | 61 | // TestJobNotRunnable tests whether a manifest is deployable to the specified namespace. 62 | func TestJobNotRunnable(t *testing.T, fw *framework.Framework, manifestLocation, namespace string) { 63 | job := buildJob(t, fw, manifestLocation, namespace, true) 64 | if job != nil { 65 | if !assert.NotZero(t, job.Status.Failed, "Job Running ") { 66 | DumpEvents(t, fw, namespace) 67 | DumpPolicies(t, fw, namespace) 68 | 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /test/e2e/utils/pod.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018, 2021 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package utils 16 | 17 | import ( 18 | "testing" 19 | 20 | "github.com/IBM/portieris/test/framework" 21 | "github.com/stretchr/testify/assert" 22 | corev1 "k8s.io/api/core/v1" 23 | ) 24 | 25 | func buildPod(t *testing.T, fw *framework.Framework, manifestLocation, namespace string, expectCreateFail bool) *corev1.Pod { 26 | manifest, err := fw.LoadPodManifest(manifestLocation) 27 | if err != nil { 28 | t.Fatalf("Error loading manifest: %v", err) 29 | } 30 | if manifest == nil { 31 | t.Fatalf("Error loading manifest: manifest is nil") 32 | } 33 | if err = fw.CreatePod(namespace, manifest); err != nil { 34 | if !expectCreateFail { 35 | t.Fatalf("Failed to create job on success path: %s", err) 36 | } else { 37 | return nil 38 | } 39 | } 40 | pod, err := fw.GetPod(manifest.Name, namespace) 41 | if err != nil { 42 | t.Fatalf("Error getting %q pod in %v: %v", manifest.Name, namespace, err) 43 | } 44 | return pod 45 | } 46 | 47 | // TestPodRunnable tests whether a manifest is deployable to the specified namespace. 48 | func TestPodRunnable(t *testing.T, fw *framework.Framework, manifestLocation, namespace string) { 49 | pod := buildPod(t, fw, manifestLocation, namespace, false) 50 | defer fw.DeletePod(pod.Name, pod.Namespace) 51 | 52 | if !assert.Equal(t, corev1.PodRunning, pod.Status.Phase, "Pod failed: Current phase was not running ") { 53 | DumpEvents(t, fw, namespace) 54 | DumpPolicies(t, fw, namespace) 55 | } 56 | } 57 | 58 | // TestPodNotRunnable tests whether a manifest is deployable to the specified namespace. 59 | func TestPodNotRunnable(t *testing.T, fw *framework.Framework, manifestLocation, namespace string) { 60 | pod := buildPod(t, fw, manifestLocation, namespace, true) 61 | if pod != nil { 62 | if !assert.Equal(t, corev1.PodFailed, pod.Status.Phase, "Pod Running ") { 63 | DumpEvents(t, fw, namespace) 64 | DumpPolicies(t, fw, namespace) 65 | 66 | } 67 | } 68 | } 69 | 70 | // KillPod stops the first pod that is returned in the pod list in the specified namespace. 71 | func KillPod(t *testing.T, fw *framework.Framework, namespace string) { 72 | if err := fw.DeleteRandomPod(namespace); err != nil { 73 | DumpEvents(t, fw, namespace) 74 | DumpPolicies(t, fw, namespace) 75 | t.Errorf("Failed to delete random pod from namespace") 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /test/e2e/utils/replicaset.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018, 2021 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package utils 16 | 17 | import ( 18 | "testing" 19 | "time" 20 | 21 | "github.com/IBM/portieris/test/framework" 22 | "github.com/stretchr/testify/assert" 23 | appsv1 "k8s.io/api/apps/v1" 24 | ) 25 | 26 | func buildReplicaSet(t *testing.T, fw *framework.Framework, manifestLocation, namespace string, expectCreateFail bool) *appsv1.ReplicaSet { 27 | manifest, err := fw.LoadReplicaSetManifest(manifestLocation) 28 | if err != nil { 29 | t.Fatalf("Error loading manifest: %v", err) 30 | } 31 | if manifest == nil { 32 | t.Fatalf("Error loading manifest: manifest is nil") 33 | } 34 | if err = fw.CreateReplicaSet(namespace, manifest); err != nil { 35 | if !expectCreateFail { 36 | t.Fatalf("Error creating %q replicaset in %v: %v", manifest.Name, namespace, err) 37 | } else { 38 | return nil 39 | } 40 | } 41 | fw.WaitForReplicaSetPods(manifest.Name, namespace, time.Minute) 42 | replicaset, err := fw.GetReplicaSet(manifest.Name, namespace) 43 | if err != nil { 44 | t.Fatalf("Error getting %q replicaset in %v: %v", manifest.Name, namespace, err) 45 | } 46 | return replicaset 47 | } 48 | 49 | // TestReplicaSetRunnable tests whether a manifest is deployable to the specified namespace. 50 | func TestReplicaSetRunnable(t *testing.T, fw *framework.Framework, manifestLocation, namespace string) { 51 | replicaset := buildReplicaSet(t, fw, manifestLocation, namespace, false) 52 | defer fw.DeleteReplicaSet(replicaset.Name, replicaset.Namespace) 53 | if !assert.Equal(t, *replicaset.Spec.Replicas, replicaset.Status.AvailableReplicas, "ReplicaSet failed: available replicas did not match expected replicas") { 54 | DumpEvents(t, fw, namespace) 55 | DumpPolicies(t, fw, namespace) 56 | } 57 | } 58 | 59 | // TestReplicaSetNotRunnable tests whether a manifest is deployable to the specified namespace. 60 | func TestReplicaSetNotRunnable(t *testing.T, fw *framework.Framework, manifestLocation, namespace string) { 61 | replicaset := buildReplicaSet(t, fw, manifestLocation, namespace, true) 62 | if replicaset != nil { 63 | defer fw.DeleteReplicaSet(replicaset.Name, replicaset.Namespace) 64 | DumpEvents(t, fw, namespace) 65 | DumpPolicies(t, fw, namespace) 66 | t.Errorf("Expected replicaset creation to fail") 67 | 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /test/e2e/utils/replicationcontroller.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018, 2021 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package utils 16 | 17 | import ( 18 | "testing" 19 | "time" 20 | 21 | "github.com/IBM/portieris/test/framework" 22 | "github.com/stretchr/testify/assert" 23 | corev1 "k8s.io/api/core/v1" 24 | ) 25 | 26 | func buildReplicationController(t *testing.T, fw *framework.Framework, manifestLocation, namespace string, expectCreateFail bool) *corev1.ReplicationController { 27 | manifest, err := fw.LoadReplicationControllerManifest(manifestLocation) 28 | if err != nil { 29 | t.Fatalf("Error loading manifest: %v", err) 30 | } 31 | if manifest == nil { 32 | t.Fatalf("Error loading manifest: manifest is nil") 33 | } 34 | if err = fw.CreateReplicationController(namespace, manifest); err != nil { 35 | if !expectCreateFail { 36 | 37 | t.Fatalf("Error creating %q replicationcontroller in %v: %v", manifest.Name, namespace, err) 38 | } else { 39 | return nil 40 | } 41 | } 42 | fw.WaitForReplicationControllerPods(manifest.Name, namespace, time.Minute) 43 | replicationcontroller, err := fw.GetReplicationController(manifest.Name, namespace) 44 | if err != nil { 45 | t.Fatalf("Error getting %q replicationcontroller in %v: %v", manifest.Name, namespace, err) 46 | } 47 | return replicationcontroller 48 | } 49 | 50 | // TestReplicationControllerRunnable tests whether a manifest is deployable to the specified namespace. 51 | func TestReplicationControllerRunnable(t *testing.T, fw *framework.Framework, manifestLocation, namespace string) { 52 | replicationcontroller := buildReplicationController(t, fw, manifestLocation, namespace, false) 53 | defer fw.DeleteReplicationController(replicationcontroller.Name, replicationcontroller.Namespace) 54 | if !assert.Equal(t, *replicationcontroller.Spec.Replicas, replicationcontroller.Status.AvailableReplicas, "ReplicationController failed: available replicas did not match expected replicas") { 55 | DumpEvents(t, fw, namespace) 56 | DumpPolicies(t, fw, namespace) 57 | } 58 | } 59 | 60 | // TestReplicationControllerNotRunnable tests whether a manifest is deployable to the specified namespace. 61 | func TestReplicationControllerNotRunnable(t *testing.T, fw *framework.Framework, manifestLocation, namespace string) { 62 | replicationcontroller := buildReplicationController(t, fw, manifestLocation, namespace, true) 63 | if replicationcontroller != nil { 64 | defer fw.DeleteReplicationController(replicationcontroller.Name, replicationcontroller.Namespace) 65 | DumpEvents(t, fw, namespace) 66 | DumpPolicies(t, fw, namespace) 67 | t.Errorf("Expected replicacontroller creation to fail") 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /test/e2e/utils/secret.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package utils 16 | 17 | import ( 18 | "testing" 19 | 20 | "github.com/IBM/portieris/test/framework" 21 | ) 22 | 23 | func CreateSecret(t *testing.T, fw *framework.Framework, manifestPath, namespace string) { 24 | manifest, err := fw.LoadSecretManifest(manifestPath) 25 | if err != nil { 26 | t.Fatalf("Error loading secret manifest: %v", err) 27 | } 28 | if manifest == nil { 29 | t.Fatalf("Error loading manifest: manifest is nil") 30 | } 31 | if err := fw.CreateSecret(namespace, manifest); err != nil { 32 | t.Fatalf("Error creating secret %q: %v", manifest.Name, err) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /test/e2e/utils/statefulset.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018, 2021 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package utils 16 | 17 | import ( 18 | "testing" 19 | "time" 20 | 21 | "github.com/IBM/portieris/test/framework" 22 | "github.com/stretchr/testify/assert" 23 | appsv1 "k8s.io/api/apps/v1" 24 | ) 25 | 26 | func buildStatefulSet(t *testing.T, fw *framework.Framework, manifestLocation, namespace string, expectCreateFail bool) *appsv1.StatefulSet { 27 | manifest, err := fw.LoadStatefulSetManifest(manifestLocation) 28 | if err != nil { 29 | t.Fatalf("Error loading manifest: %v", err) 30 | } 31 | if manifest == nil { 32 | t.Fatalf("Error loading manifest: manifest is nil") 33 | } 34 | if err = fw.CreateStatefulSet(namespace, manifest); err != nil { 35 | if !expectCreateFail { 36 | 37 | t.Fatalf("Error creating %q statefulset in %v: %v", manifest.Name, namespace, err) 38 | } else { 39 | return nil 40 | } 41 | } 42 | fw.WaitForStatefulSetPods(manifest.Name, namespace, time.Minute) 43 | statefulset, err := fw.GetStatefulSet(manifest.Name, namespace) 44 | if err != nil { 45 | t.Fatalf("Error getting %q statefulset in %v: %v", manifest.Name, namespace, err) 46 | } 47 | return statefulset 48 | } 49 | 50 | // TestStatefulSetRunnable tests whether a manifest is deployable to the specified namespace. 51 | func TestStatefulSetRunnable(t *testing.T, fw *framework.Framework, manifestLocation, namespace string) { 52 | statefulset := buildStatefulSet(t, fw, manifestLocation, namespace, false) 53 | defer fw.DeleteStatefulSet(statefulset.Name, statefulset.Namespace) 54 | if !assert.Equal(t, *statefulset.Spec.Replicas, statefulset.Status.ReadyReplicas, "StatefulSet failed: available replicas did not match expected replicas") { 55 | DumpEvents(t, fw, namespace) 56 | DumpPolicies(t, fw, namespace) 57 | } 58 | } 59 | 60 | // TestStatefulSetNotRunnable tests whether a manifest is deployable to the specified namespace. 61 | func TestStatefulSetNotRunnable(t *testing.T, fw *framework.Framework, manifestLocation, namespace string) { 62 | statefulset := buildStatefulSet(t, fw, manifestLocation, namespace, true) 63 | if statefulset != nil { 64 | defer fw.DeleteStatefulSet(statefulset.Name, statefulset.Namespace) 65 | 66 | DumpEvents(t, fw, namespace) 67 | DumpPolicies(t, fw, namespace) 68 | t.Errorf("Expected statefulset creation to fail") 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /test/e2e/wildcard.generic_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018, 2021 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package e2e 16 | 17 | import ( 18 | "testing" 19 | 20 | "github.com/IBM/portieris/test/e2e/utils" 21 | ) 22 | 23 | func TestWildcards_ImagePolicyRepositories_Wildcards(t *testing.T) { 24 | utils.CheckIfTesting(t, testWildcardImagePolicy) 25 | if defaultClusterPolicy := utils.DeleteThenReturnClusterImagePolicy(t, framework, "default"); defaultClusterPolicy != nil { 26 | defer framework.CreateClusterImagePolicy(defaultClusterPolicy) 27 | } 28 | 29 | t.Run("Correctly apply policy when single trailing wildcard is used", func(t *testing.T) { 30 | t.Parallel() 31 | namespace := utils.CreateImagePolicyInstalledNamespace(t, framework, "./testdata/imagepolicy/allow-unsigned-trailing-wildcard.yaml", "") 32 | utils.TestDeploymentRunnable(t, framework, "./testdata/deployment/global-nginx-unsigned.yaml", namespace.Name) 33 | utils.CleanUpImagePolicyTest(t, framework, namespace.Name) 34 | }) 35 | t.Run("Correctly apply policy when single embedded wildcard is used", func(t *testing.T) { 36 | t.Parallel() 37 | namespace := utils.CreateImagePolicyInstalledNamespace(t, framework, "./testdata/imagepolicy/allow-unsigned-embedded-wildcard.yaml", "") 38 | utils.TestDeploymentRunnable(t, framework, "./testdata/deployment/global-nginx-unsigned.yaml", namespace.Name) 39 | utils.CleanUpImagePolicyTest(t, framework, namespace.Name) 40 | }) 41 | t.Run("Correctly apply policy when both embedded and trailing wildcard are used", func(t *testing.T) { 42 | t.Parallel() 43 | namespace := utils.CreateImagePolicyInstalledNamespace(t, framework, "./testdata/imagepolicy/allow-unsigned-embedded-trailing-wildcard.yaml", "") 44 | utils.TestDeploymentRunnable(t, framework, "./testdata/deployment/global-nginx-unsigned.yaml", namespace.Name) 45 | utils.CleanUpImagePolicyTest(t, framework, namespace.Name) 46 | }) 47 | 48 | } 49 | -------------------------------------------------------------------------------- /test/framework/clusterrole.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018, 2021 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package framework 16 | 17 | import ( 18 | "context" 19 | 20 | v1beta1 "k8s.io/api/rbac/v1beta1" 21 | ) 22 | 23 | // ListClusterRoles lists all the cluster roles that are associated with the installed Helm release. 24 | func (f *Framework) ListClusterRoles() (*v1beta1.ClusterRoleList, error) { 25 | opts := f.getHelmReleaseSelectorListOptions() 26 | return f.KubeClient.RbacV1beta1().ClusterRoles().List(context.TODO(), opts) 27 | } 28 | -------------------------------------------------------------------------------- /test/framework/clusterrolebinding.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018, 2021 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package framework 16 | 17 | import ( 18 | "context" 19 | 20 | v1beta1 "k8s.io/api/rbac/v1beta1" 21 | ) 22 | 23 | // ListClusterRoleBindings lists all the cluster role bindings that are associated with the installed Helm release. 24 | func (f *Framework) ListClusterRoleBindings() (*v1beta1.ClusterRoleBindingList, error) { 25 | opts := f.getHelmReleaseSelectorListOptions() 26 | return f.KubeClient.RbacV1beta1().ClusterRoleBindings().List(context.TODO(), opts) 27 | } 28 | -------------------------------------------------------------------------------- /test/framework/configmap.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018, 2021 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package framework 16 | 17 | import ( 18 | "context" 19 | 20 | corev1 "k8s.io/api/core/v1" 21 | ) 22 | 23 | // ListConfigMaps lists all the configuration maps that are associated with the installed Helm release. 24 | func (f *Framework) ListConfigMaps() (*corev1.ConfigMapList, error) { 25 | opts := f.getHelmReleaseSelectorListOptions() 26 | return f.KubeClient.CoreV1().ConfigMaps(corev1.NamespaceAll).List(context.TODO(), opts) 27 | } 28 | -------------------------------------------------------------------------------- /test/framework/cronjob.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018, 2023 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package framework 16 | 17 | import ( 18 | "context" 19 | "fmt" 20 | 21 | "time" 22 | 23 | batchv1 "k8s.io/api/batch/v1" 24 | corev1 "k8s.io/api/core/v1" 25 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 | "k8s.io/apimachinery/pkg/util/wait" 27 | "k8s.io/apimachinery/pkg/util/yaml" 28 | ) 29 | 30 | // ListCronJobs lists all the Cron Jobs that are associated with the installed Helm release. 31 | func (f *Framework) ListCronJobs() (*batchv1.CronJobList, error) { 32 | opts := f.getHelmReleaseSelectorListOptions() 33 | return f.KubeClient.BatchV1().CronJobs(corev1.NamespaceAll).List(context.TODO(), opts) 34 | } 35 | 36 | // GetCronJob retrieves the specified Cron Job. 37 | func (f *Framework) GetCronJob(name, namespace string) (*batchv1.CronJob, error) { 38 | return f.KubeClient.BatchV1().CronJobs(namespace).Get(context.TODO(), name, metav1.GetOptions{}) 39 | } 40 | 41 | // LoadCronJobManifest takes a manifest and decodes it into a Cron Job object. 42 | func (f *Framework) LoadCronJobManifest(pathToManifest string) (*batchv1.CronJob, error) { 43 | manifest, err := openFile(pathToManifest) 44 | if err != nil { 45 | return nil, fmt.Errorf("Unable to open file %q: %v", pathToManifest, err) 46 | } 47 | job := batchv1.CronJob{} 48 | if err := yaml.NewYAMLOrJSONDecoder(manifest, 100).Decode(&job); err != nil { 49 | return nil, fmt.Errorf("Unable to decode file %q: %v", pathToManifest, err) 50 | } 51 | return &job, nil 52 | } 53 | 54 | // CreateCronJob creates a Cron Job resource and then waits for it to show. 55 | func (f *Framework) CreateCronJob(namespace string, job *batchv1.CronJob) error { 56 | if _, err := f.KubeClient.BatchV1().CronJobs(namespace).Create(context.TODO(), job, metav1.CreateOptions{}); err != nil { 57 | return err 58 | } 59 | return f.WaitForCronJob(job.Name, namespace, 2*time.Minute) 60 | } 61 | 62 | // DeleteCronJob deletes the specified Cron Job. 63 | func (f *Framework) DeleteCronJob(name, namespace string) error { 64 | return f.KubeClient.BatchV1().CronJobs(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{}) 65 | } 66 | 67 | // WaitForCronJob waits until the Cron Job deployment is complete. 68 | func (f *Framework) WaitForCronJob(name, namespace string, timeout time.Duration) error { 69 | return wait.Poll(time.Second*5, timeout, func() (bool, error) { 70 | job, err := f.GetCronJob(name, namespace) 71 | if err != nil { 72 | return false, err 73 | } 74 | if len(job.Status.Active) == 0 { 75 | return false, nil 76 | } 77 | return true, nil 78 | }) 79 | } 80 | -------------------------------------------------------------------------------- /test/framework/debug.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018, 2021 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package framework 16 | 17 | import ( 18 | "context" 19 | "fmt" 20 | "io" 21 | "log" 22 | "sort" 23 | "text/tabwriter" 24 | 25 | corev1 "k8s.io/api/core/v1" 26 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 | ) 28 | 29 | // DumpEvents returns a reader that has events that are written to a specified namespace. 30 | func (f *Framework) DumpEvents(namespace string) io.Reader { 31 | fmt.Printf("Dumping events for namespace: %v\n", namespace) 32 | events, err := f.KubeClient.CoreV1().Events(namespace).List(context.TODO(), metav1.ListOptions{}) 33 | if err != nil { 34 | log.Printf("error retrieving events from %q: %v", namespace, err) 35 | } 36 | 37 | sort.Sort(evs(events.Items)) 38 | 39 | reader, writer := io.Pipe() 40 | w := tabwriter.NewWriter(writer, 0, 0, 2, ' ', 0) 41 | go func() { 42 | fmt.Fprint(w, "\nTIME\tNAME\tKIND\tREASON\tSOURCE\tMESSAGE\n") 43 | for _, event := range events.Items { 44 | fmt.Fprintf(w, "%v\t%v\t%v\t%v\t%v\t%v\n", 45 | event.FirstTimestamp, 46 | event.Name, 47 | event.InvolvedObject.Kind, 48 | event.Reason, 49 | event.Source, 50 | event.Message, 51 | ) 52 | } 53 | w.Flush() 54 | writer.Close() 55 | }() 56 | return reader 57 | } 58 | 59 | type evs []corev1.Event 60 | 61 | func (e evs) Len() int { 62 | return len(e) 63 | } 64 | 65 | func (e evs) Swap(i, j int) { 66 | e[i], e[j] = e[j], e[i] 67 | } 68 | 69 | func (e evs) Less(i, j int) bool { 70 | return e[i].LastTimestamp.UnixNano() < e[j].LastTimestamp.UnixNano() 71 | } 72 | 73 | // DumpPolicies returns a reader that has all cluster and image policies present in it. 74 | func (f *Framework) DumpPolicies(namespace string) io.Reader { 75 | fmt.Printf("Dumping cluster policies and policies for namespace: %v\n", namespace) 76 | 77 | clusterImagePolicies, err := f.ListClusterImagePolicies() 78 | if err != nil { 79 | log.Printf("error listing ClusterImagePolicies: %v", err) 80 | } 81 | imagePolicies, err := f.ListImagePolicies(namespace) 82 | if err != nil { 83 | log.Printf("error listing ImagePolicies in %q: %v", namespace, err) 84 | } 85 | 86 | reader, writer := io.Pipe() 87 | go func() { 88 | fmt.Fprint(writer, "\nClusterImagePolicies Present:\n") 89 | for _, clusterImagePolicy := range clusterImagePolicies.Items { 90 | fmt.Fprintf(writer, "- %v\n", clusterImagePolicy.Name) 91 | } 92 | fmt.Fprint(writer, "\nImagePolicies Present:\n") 93 | for _, imagePolicy := range imagePolicies.Items { 94 | fmt.Fprintf(writer, "- %v\n", imagePolicy.Name) 95 | } 96 | writer.Close() 97 | }() 98 | return reader 99 | 100 | } 101 | -------------------------------------------------------------------------------- /test/framework/helm.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package framework 16 | 17 | import ( 18 | "bufio" 19 | "fmt" 20 | "os/exec" 21 | ) 22 | 23 | func (f *Framework) installChart() error { 24 | cmdName := "helm" 25 | cmdArgs := []string{"install", f.HelmChart, "-n", f.HelmRelease} 26 | return f.runCommand(cmdName, cmdArgs) 27 | } 28 | 29 | func (f *Framework) deleteChart() error { 30 | cmdName := "helm" 31 | cmdArgs := []string{"delete", "--purge", f.HelmRelease} 32 | return f.runCommand(cmdName, cmdArgs) 33 | } 34 | 35 | func (f *Framework) runCommand(name string, args []string) error { 36 | cmd := exec.Command(name, args...) 37 | cmdOut, err := cmd.StdoutPipe() 38 | if err != nil { 39 | return fmt.Errorf("error creating stdout pipe for helm install: %v", err) 40 | } 41 | cmdErr, err := cmd.StderrPipe() 42 | if err != nil { 43 | return fmt.Errorf("error creating stderr pipe for helm install: %v", err) 44 | } 45 | 46 | commandLog := fmt.Sprintf("%v %v", name, args[0]) 47 | outScanner := bufio.NewScanner(cmdOut) 48 | go func() { 49 | for outScanner.Scan() { 50 | fmt.Printf("%s\n", outScanner.Text()) 51 | } 52 | }() 53 | 54 | errScanner := bufio.NewScanner(cmdErr) 55 | go func() { 56 | for errScanner.Scan() { 57 | fmt.Printf("%v | %s\n", commandLog, errScanner.Text()) 58 | } 59 | }() 60 | 61 | err = cmd.Start() 62 | if err != nil { 63 | return fmt.Errorf("error starting helm install: %v", err) 64 | } 65 | 66 | err = cmd.Wait() 67 | if err != nil { 68 | return fmt.Errorf("error waiting for helm install: %v", err) 69 | } 70 | return nil 71 | } 72 | -------------------------------------------------------------------------------- /test/framework/job.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018, 2021 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package framework 16 | 17 | import ( 18 | "context" 19 | "fmt" 20 | "time" 21 | 22 | batchv1 "k8s.io/api/batch/v1" 23 | corev1 "k8s.io/api/core/v1" 24 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 25 | "k8s.io/apimachinery/pkg/util/wait" 26 | "k8s.io/apimachinery/pkg/util/yaml" 27 | ) 28 | 29 | // ListJobs lists all jobs that are associated with the installed Helm release. 30 | func (f *Framework) ListJobs() (*batchv1.JobList, error) { 31 | opts := f.getHelmReleaseSelectorListOptions() 32 | return f.KubeClient.BatchV1().Jobs(corev1.NamespaceAll).List(context.TODO(), opts) 33 | } 34 | 35 | // GetJob retrieves the specified job. 36 | func (f *Framework) GetJob(name, namespace string) (*batchv1.Job, error) { 37 | return f.KubeClient.BatchV1().Jobs(namespace).Get(context.TODO(), name, metav1.GetOptions{}) 38 | } 39 | 40 | // LoadJobManifest takes a manifest and decodes it into a job. 41 | func (f *Framework) LoadJobManifest(pathToManifest string) (*batchv1.Job, error) { 42 | manifest, err := openFile(pathToManifest) 43 | if err != nil { 44 | return nil, fmt.Errorf("Unable to open file %q: %v", pathToManifest, err) 45 | } 46 | job := batchv1.Job{} 47 | if err := yaml.NewYAMLOrJSONDecoder(manifest, 100).Decode(&job); err != nil { 48 | return nil, fmt.Errorf("Unable to decode file %q: %v", pathToManifest, err) 49 | } 50 | return &job, nil 51 | } 52 | 53 | // CreateJob creates a job and waits for it to show. 54 | func (f *Framework) CreateJob(namespace string, job *batchv1.Job) error { 55 | if _, err := f.KubeClient.BatchV1().Jobs(namespace).Create(context.TODO(), job, metav1.CreateOptions{}); err != nil { 56 | return err 57 | } 58 | return f.WaitForJob(job.Name, namespace, time.Minute) 59 | } 60 | 61 | // DeleteJob deletes the specified job. 62 | func (f *Framework) DeleteJob(name, namespace string) error { 63 | return f.KubeClient.BatchV1().Jobs(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{}) 64 | } 65 | 66 | // WaitForJob waits until the job completes. 67 | func (f *Framework) WaitForJob(name, namespace string, timeout time.Duration) error { 68 | return wait.Poll(time.Second*5, timeout, func() (bool, error) { 69 | job, err := f.GetJob(name, namespace) 70 | if err != nil { 71 | return false, err 72 | } 73 | if job.Status.Active == 0 && job.Status.Succeeded == 0 { 74 | return false, nil 75 | } 76 | 77 | return true, nil 78 | }) 79 | } 80 | -------------------------------------------------------------------------------- /test/framework/mutatingadmissionwebhook.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018, 2021 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package framework 16 | 17 | import ( 18 | "context" 19 | "log" 20 | "time" 21 | 22 | v1 "k8s.io/api/admissionregistration/v1" 23 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 24 | "k8s.io/apimachinery/pkg/util/wait" 25 | ) 26 | 27 | // WaitForMutatingAdmissionWebhook waits until the specified mutating admission webhook is created or the timeout is reached. 28 | func (f *Framework) WaitForMutatingAdmissionWebhook(name string, timeout time.Duration) error { 29 | return wait.Poll(time.Second*5, timeout, func() (bool, error) { 30 | if _, err := f.KubeClient.AdmissionregistrationV1().MutatingWebhookConfigurations().Get(context.TODO(), name, metav1.GetOptions{}); err != nil { 31 | return false, err 32 | } 33 | log.Printf("Found MutatingWebhookConfiguration %q", name) 34 | return true, nil 35 | }) 36 | } 37 | 38 | // ListMutatingAdmissionWebhooks lists the mutating admission webhooks that are associated with the installed Helm release. 39 | func (f *Framework) ListMutatingAdmissionWebhooks() (*v1.MutatingWebhookConfigurationList, error) { 40 | opts := f.getHelmReleaseSelectorListOptions() 41 | return f.KubeClient.AdmissionregistrationV1().MutatingWebhookConfigurations().List(context.TODO(), opts) 42 | } 43 | -------------------------------------------------------------------------------- /test/framework/secret.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018, 2021 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package framework 16 | 17 | import ( 18 | "context" 19 | "fmt" 20 | "time" 21 | 22 | corev1 "k8s.io/api/core/v1" 23 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 24 | "k8s.io/apimachinery/pkg/util/wait" 25 | "k8s.io/apimachinery/pkg/util/yaml" 26 | ) 27 | 28 | // LoadSecretManifest takes a manifest and decodes it into a secret. 29 | func (f *Framework) LoadSecretManifest(pathToManifest string) (*corev1.Secret, error) { 30 | manifest, err := openFile(pathToManifest) 31 | if err != nil { 32 | return nil, fmt.Errorf("Unable to open file %q: %v", pathToManifest, err) 33 | } 34 | secret := corev1.Secret{} 35 | if err := yaml.NewYAMLOrJSONDecoder(manifest, 100).Decode(&secret); err != nil { 36 | return nil, fmt.Errorf("Unable to decode file %q: %v", pathToManifest, err) 37 | } 38 | return &secret, nil 39 | } 40 | 41 | // CreateSecret creates a secret and waits for it to show. 42 | func (f *Framework) CreateSecret(namespace string, secret *corev1.Secret) error { 43 | if _, err := f.KubeClient.CoreV1().Secrets(namespace).Create(context.TODO(), secret, metav1.CreateOptions{}); err != nil { 44 | return err 45 | } 46 | return f.WaitForSecret(secret.Name, namespace, time.Minute) 47 | } 48 | 49 | // GetSecret retrieves the specified secret. 50 | func (f *Framework) GetSecret(name, namespace string) (*corev1.Secret, error) { 51 | return f.KubeClient.CoreV1().Secrets(namespace).Get(context.TODO(), name, metav1.GetOptions{}) 52 | } 53 | 54 | // WaitForSecret waits until the specified secret is created or the timeout is reached. 55 | func (f *Framework) WaitForSecret(name, namespace string, timeout time.Duration) error { 56 | return wait.Poll(time.Second*5, timeout, func() (bool, error) { 57 | if _, err := f.GetSecret(name, namespace); err != nil { 58 | return false, err 59 | } 60 | return true, nil 61 | }) 62 | } 63 | -------------------------------------------------------------------------------- /test/framework/service.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018, 2021 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package framework 16 | 17 | import ( 18 | "context" 19 | 20 | corev1 "k8s.io/api/core/v1" 21 | ) 22 | 23 | // ListServices lists all services that are associated with the installed Helm release. 24 | func (f *Framework) ListServices() (*corev1.ServiceList, error) { 25 | opts := f.getHelmReleaseSelectorListOptions() 26 | return f.KubeClient.CoreV1().Services(corev1.NamespaceAll).List(context.TODO(), opts) 27 | } 28 | -------------------------------------------------------------------------------- /test/framework/serviceaccount.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018, 2021 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package framework 16 | 17 | import ( 18 | "context" 19 | 20 | corev1 "k8s.io/api/core/v1" 21 | ) 22 | 23 | func generateServiceAccount(name string) *corev1.ServiceAccount { 24 | sa := &corev1.ServiceAccount{} 25 | sa.Name = name 26 | sa.Kind = "ServiceAccount" 27 | return sa 28 | } 29 | 30 | // ListServiceAccounts lists all service accounts that are associated with the installed Helm release. 31 | func (f *Framework) ListServiceAccounts() (*corev1.ServiceAccountList, error) { 32 | opts := f.getHelmReleaseSelectorListOptions() 33 | return f.KubeClient.CoreV1().ServiceAccounts(corev1.NamespaceAll).List(context.TODO(), opts) 34 | } 35 | -------------------------------------------------------------------------------- /test/framework/utils.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018, 2022 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package framework 16 | 17 | import ( 18 | "fmt" 19 | "os" 20 | "path/filepath" 21 | 22 | "github.com/gofrs/uuid" 23 | 24 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 25 | ) 26 | 27 | func (f *Framework) getHelmReleaseSelectorListOptions() metav1.ListOptions { 28 | return metav1.ListOptions{ 29 | LabelSelector: fmt.Sprintf("release=%v", f.HelmRelease), 30 | } 31 | } 32 | 33 | // MakeTestUUID is a simple wrapper to return a UUID for testing purposes 34 | func MakeTestUUID() string { 35 | u, err := uuid.NewV4() 36 | if err != nil { 37 | panic(err) 38 | } 39 | return u.String() 40 | } 41 | 42 | // GenerateTestAnnotation returns a unique test annotation that is used to patch resources. 43 | func (f *Framework) GenerateTestAnnotation() string { 44 | return fmt.Sprintf(` 45 | { 46 | "metadata": { 47 | "annotations": { 48 | "test":"%v" 49 | } 50 | } 51 | } 52 | `, MakeTestUUID()) 53 | } 54 | 55 | func openFile(relativePath string) (*os.File, error) { 56 | absolutePath, err := filepath.Abs(relativePath) 57 | if err != nil { 58 | return nil, fmt.Errorf("unable to work out absolute path of %q", relativePath) 59 | } 60 | return os.Open(absolutePath) 61 | } 62 | -------------------------------------------------------------------------------- /test/framework/validatingadmissionwebhook.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018, 2021 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package framework 16 | 17 | import ( 18 | "context" 19 | "log" 20 | "time" 21 | 22 | v1 "k8s.io/api/admissionregistration/v1" 23 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 24 | "k8s.io/apimachinery/pkg/util/wait" 25 | ) 26 | 27 | // WaitForValidatingAdmissionWebhook waits until the specified validating admission webhook is created or the timeout is reached. 28 | func (f *Framework) WaitForValidatingAdmissionWebhook(name string, timeout time.Duration) error { 29 | return wait.Poll(time.Second*5, timeout, func() (bool, error) { 30 | if _, err := f.KubeClient.AdmissionregistrationV1().ValidatingWebhookConfigurations().Get(context.TODO(), name, metav1.GetOptions{}); err != nil { 31 | return false, err 32 | } 33 | log.Printf("Found ValidatingWebhookConfiguration %q", name) 34 | return true, nil 35 | }) 36 | } 37 | 38 | // ListValidatingAdmissionWebhooks lists the validating admission webhooks that are associated with the installed Helm release. 39 | func (f *Framework) ListValidatingAdmissionWebhooks() (*v1.ValidatingWebhookConfigurationList, error) { 40 | opts := f.getHelmReleaseSelectorListOptions() 41 | return f.KubeClient.AdmissionregistrationV1().ValidatingWebhookConfigurations().List(context.TODO(), opts) 42 | } 43 | -------------------------------------------------------------------------------- /test/helm/tiller-rbac.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: tiller 5 | namespace: kube-system 6 | --- 7 | apiVersion: rbac.authorization.k8s.io/v1beta1 8 | kind: ClusterRoleBinding 9 | metadata: 10 | name: tiller 11 | roleRef: 12 | apiGroup: rbac.authorization.k8s.io 13 | kind: ClusterRole 14 | name: cluster-admin 15 | subjects: 16 | - kind: ServiceAccount 17 | name: tiller 18 | namespace: kube-system -------------------------------------------------------------------------------- /types/JSONPatch.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Portieris Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package types 16 | 17 | // JSONPatch is specified in RFC 6902 from the IETF. 18 | type JSONPatch struct { 19 | From string `json:"from,omitempty"` 20 | Op string `json:"op"` 21 | Path string `json:"path"` 22 | Value interface{} `json:"value,omitempty"` 23 | } 24 | --------------------------------------------------------------------------------