Skip to content
Permalink

Comparing changes

This is a direct comparison between two commits made in this repository or its related repositories. View the default comparison for this range or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: devfile/registry-operator
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: c82382558c121418f330da604ee794ac3b6985ef
Choose a base ref
..
head repository: devfile/registry-operator
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 139ed803a983aaea91494217b3c77ddca2d6a36d
Choose a head ref
Showing with 2,581 additions and 1,569 deletions.
  1. +7 −0 .clomonitor.yml
  2. +13 −1 .github/PULL_REQUEST_TEMPLATE.md
  3. +85 −89 .github/workflows/ci.yaml
  4. +32 −27 .github/workflows/dockerimage-push.yaml
  5. +58 −0 .github/workflows/scorecard.yml
  6. +82 −14 CONTRIBUTING.md
  7. +37 −0 DCO
  8. +13 −0 DEVFILE_REGISTRY.md
  9. +3 −3 Dockerfile
  10. +73 −9 Makefile
  11. +4 −4 OWNERS
  12. +51 −16 README.md
  13. +1 −1 VERSION
  14. +10 −9 api/v1alpha1/clusterdevfileregistrieslist_webhook.go
  15. +10 −9 api/v1alpha1/devfileregistrieslist_webhook.go
  16. +19 −0 api/v1alpha1/devfileregistry_types.go
  17. +7 −6 api/v1alpha1/devfileregistry_webhook.go
  18. +12 −6 api/v1alpha1/webhook_suite_test.go
  19. +1 −1 bundle.Dockerfile
  20. +27 −3 bundle/manifests/registry-operator.clusterserviceversion.yaml
  21. +24 −0 bundle/manifests/registry.devfile.io_devfileregistries.yaml
  22. +1 −1 bundle/metadata/annotations.yaml
  23. +24 −0 config/crd/bases/registry.devfile.io_devfileregistries.yaml
  24. +26 −2 config/manifests/bases/registry-operator.clusterserviceversion.yaml
  25. +1 −0 controllers/condition_types.go
  26. +18 −2 controllers/devfileregistry_controller.go
  27. +8 −8 controllers/ensure.go
  28. +1 −1 controllers/update.go
  29. +71 −66 go.mod
  30. +178 −1,218 go.sum
  31. +13 −7 main.go
  32. +1 −1 pkg/registry/configmap.go
  33. +21 −0 pkg/registry/constants.go
  34. +141 −1 pkg/registry/defaults.go
  35. +497 −23 pkg/registry/defaults_test.go
  36. +4 −4 pkg/registry/deployment.go
  37. +9 −3 pkg/registry/ingress.go
  38. +90 −0 pkg/registry/ingress_test.go
  39. +29 −17 pkg/registry/naming.go
  40. +643 −0 pkg/registry/naming_test.go
  41. +2 −2 pkg/registry/route.go
  42. +1 −1 pkg/registry/service.go
  43. +32 −3 pkg/registry/util.go
  44. +190 −0 pkg/registry/util_test.go
  45. +2 −2 pkg/registry/volume.go
  46. +1 −1 tests/integration/examples/create/devfileregistry-headless.yaml
  47. +1 −1 tests/integration/examples/create/devfileregistry-tls.yaml
  48. +1 −1 tests/integration/examples/create/devfileregistry.yaml
  49. +1 −1 tests/integration/examples/update/devfileregistry-new.yaml
  50. +1 −1 tests/integration/examples/update/devfileregistry-old.yaml
  51. +4 −4 tests/integration/pkg/tests/devfileregistry_tests.go
7 changes: 7 additions & 0 deletions .clomonitor.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# CLOMonitor metadata file
# This file must be located at the root of the repository

# Checks exemptions
exemptions:
- check: license_scanning # Check identifier (see https://github.com/cncf/clomonitor/blob/main/docs/checks.md#exemptions)
reason: "There are currently no plans moving forward to implement FOSSA or Snyk for scanning purposes." # Justification of this exemption (mandatory, it will be displayed on the UI)
14 changes: 13 additions & 1 deletion .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -16,4 +16,16 @@ Fixes #?
Documentation
- [ ] Does the registry operator documentation need to be updated with your changes?

**How to test changes / Special notes to the reviewer**:
<!--
Instructions for locally testing changes made to the operator, drawn from CONTRIBUTING.md
-->
**Testing changes**

[Running Unit Tests](https://github.com/devfile/registry-operator/blob/main/CONTRIBUTING.md#unit-tests)

[Running Integration Tests](https://github.com/devfile/registry-operator/blob/main/CONTRIBUTING.md#integration-tests)

<!--
Add extra instructions that reviewers may need regarding testing your changes or to properly review your pull request.
-->
**Special notes to the reviewer**:
174 changes: 85 additions & 89 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -16,112 +16,108 @@ name: Validate PRs

on:
pull_request:
branches: [ main ]
branches: [main]

jobs:
go:
name: Check go sources
runs-on: ubuntu-latest
steps:
-
name: Check out code into the Go module directory
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
-
name: Set up Go 1.x
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
with:
go-version-file: 'go.mod'
-
name: Cache go modules
id: cache-mod
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
-
name: Download dependencies
run: go mod download
if: steps.cache-mod.outputs.cache-hit != 'true'
-
name: Check go mod status
run: |
go mod tidy
if [[ ! -z $(git status -s) ]]
then
echo "Go mod state is not clean: $(git status -s)"
exit 1
fi
-
name: Check format
run: |
go get -u github.com/google/addlicense
go install github.com/google/addlicense
go get -u golang.org/x/tools/cmd/goimports
go install golang.org/x/tools/cmd/goimports
git reset HEAD --hard
- name: Check out code into the Go module directory
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
- name: Set up Go 1.x
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
with:
go-version-file: "go.mod"
- name: Cache go modules
id: cache-mod
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Download dependencies
run: go mod download
if: steps.cache-mod.outputs.cache-hit != 'true'
- name: Check go mod status
run: |
go mod tidy
if [[ ! -z $(git status -s) ]]
then
echo "Go mod state is not clean: $(git status -s)"
exit 1
fi
- name: Check format
run: |
go get -u github.com/google/addlicense
go install github.com/google/addlicense
go get -u golang.org/x/tools/cmd/goimports
go install golang.org/x/tools/cmd/goimports
git reset HEAD --hard
make fmt
if [[ ! -z $(git status -s) ]]
then
echo "not well formatted sources found during make fmt: $(git --no-pager diff)"
exit 1
fi
make fmt
if [[ ! -z $(git status -s) ]]
then
echo "not well formatted sources found during make fmt: $(git --no-pager diff)"
exit 1
fi
make fmt_license
if [[ ! -z $(git status -s) ]]
then
echo "not well formatted sources found during make fmt_license: $(git status -s)"
exit 1
fi
-
name: Check code generation
run: make generate
-
name: Check CRD manifest generation
run: make manifests
-
name: Run unit tests
run: make test
-
name: Upload coverage to Codecov
uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d # v3.1.4
make fmt_license
if [[ ! -z $(git status -s) ]]
then
echo "not well formatted sources found during make fmt_license: $(git status -s)"
exit 1
fi
- name: Check code generation
run: make generate
- name: Check CRD manifest generation
run: make manifests
- name: Run unit tests
run: make test
- name: Upload coverage to Codecov
uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d # v3.1.4

- name: Run Gosec Security Scanner
run: |
go install github.com/securego/gosec/v2/cmd/gosec@v2.14.0
make gosec
if [[ $? != 0 ]]
then
echo "gosec scanner failed to run "
exit 1
fi
- name: Run Gosec Security Scanner
run: |
go install github.com/securego/gosec/v2/cmd/gosec@v2.14.0
make gosec
if [[ $? != 0 ]]
then
echo "gosec scanner failed to run "
exit 1
fi
- name: Upload SARIF file
uses: github/codeql-action/upload-sarif@74483a38d39275f33fcff5f35b679b5ca4a26a99 # 2.22.5
with:
# Path to SARIF file relative to the root of the repository
sarif_file: gosec.sarif
- name: Upload SARIF file
uses: github/codeql-action/upload-sarif@74483a38d39275f33fcff5f35b679b5ca4a26a99 # 2.22.5
with:
# Path to SARIF file relative to the root of the repository
sarif_file: gosec.sarif

operator-build:
name: Check operator container image build
runs-on: ubuntu-latest

steps:
-
name: Check out code into the Go module directory
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
-
name: Check if operator docker build is working
run: docker build -f Dockerfile .
- name: Check out code into the Go module directory
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3

- name: Set up QEMU # Enables arm64 image building
uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3 #v3.0.0

- name: Check if operator docker build is working
run: make docker-buildx-build

operator-bundle-build:
name: Check operator bundle build
runs-on: ubuntu-latest

steps:
-
name: Check out code into the Go module directory
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
-
name: Build the operator's bundle image
run: make bundle-build
- name: Check out code into the Go module directory
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3

- name: Set up QEMU # Enables arm64 image building
uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3 #v3.0.0

- name: Build the operator's bundle image
run: make docker-bundle-buildx-build
59 changes: 32 additions & 27 deletions .github/workflows/dockerimage-push.yaml
Original file line number Diff line number Diff line change
@@ -16,38 +16,43 @@ name: Next Dockerimage

on:
push:
branches: [ main ]
branches: [main]

jobs:

push-operator-image:
runs-on: ubuntu-latest
steps:
- name: Checkout registry-operator source code
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
- name: Docker Build & Push - Registry Operator Image
uses: docker/build-push-action@3e7a4f6646880c6f63758d73ac32392d323eaf8f # v1.1.2
with:
username: ${{ secrets.QUAY_USERNAME }}
password: ${{ secrets.QUAY_PASSWORD }}
registry: quay.io
repository: devfile/registry-operator
dockerfile: Dockerfile
tags: next
tag_with_sha: true

- name: Checkout registry-operator source code
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3

- name: Set up QEMU # Enables arm64 image building
uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3 #v3.0.0

- name: Login to Quay.io
uses: docker/login-action@465a07811f14bebb1938fbed4728c6a1ff8901fc # v2.2.0
with:
registry: quay.io
username: ${{ secrets.QUAY_USERNAME }}
password: ${{ secrets.QUAY_PASSWORD }}

- name: Build and push Operator with Docker Buildx
run: "make docker-buildx-push"

push-operator-bundle:
runs-on: ubuntu-latest
steps:
- name: Checkout registry-operator source code
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
- name: Build and push the Registry Operator Bundle to quay.io
uses: docker/build-push-action@3e7a4f6646880c6f63758d73ac32392d323eaf8f # v1.1.2
with:
username: ${{ secrets.QUAY_USERNAME }}
password: ${{ secrets.QUAY_PASSWORD }}
registry: quay.io
repository: devfile/registry-operator-bundle
dockerfile: bundle.Dockerfile
tags: next
tag_with_sha: true
- name: Checkout registry-operator source code
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3

- name: Set up QEMU # Enables arm64 image building
uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3 #v3.0.0

- name: Login to Quay.io
uses: docker/login-action@465a07811f14bebb1938fbed4728c6a1ff8901fc # v2.2.0
with:
registry: quay.io
username: ${{ secrets.QUAY_USERNAME }}
password: ${{ secrets.QUAY_PASSWORD }}

- name: Build and push bundle with Docker Buildx
run: "make docker-bundle-buildx-push"
58 changes: 58 additions & 0 deletions .github/workflows/scorecard.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: Scorecard supply-chain security
on:
# For Branch-Protection check. Only the default branch is supported. See
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
branch_protection_rule:
pull_request:
branches: [ "main" ]

# Declare default permissions as read only.
permissions: read-all

jobs:
analysis:
name: Scorecard analysis
runs-on: ubuntu-latest
permissions:
# Needed to upload the results to code-scanning dashboard.
security-events: write
# Needed to publish results and get a badge (see publish_results below).
id-token: write

steps:
- name: "Checkout code"
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
with:
persist-credentials: false

- name: "Run analysis"
uses: ossf/scorecard-action@e38b1902ae4f44df626f11ba0734b14fb91f8f86 # v2.1.2
with:
results_file: results.sarif
results_format: sarif
# (Optional) "write" PAT token. Uncomment the `repo_token` line below if:
# - you want to enable the Branch-Protection check on a *public* repository, or
# - you are installing Scorecard on a *private* repository
# To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat.
# repo_token: ${{ secrets.SCORECARD_TOKEN }}

# Public repositories:
# - Publish results to OpenSSF REST API for easy access by consumers
# - Allows the repository to include the Scorecard badge.
# - See https://github.com/ossf/scorecard-action#publishing-results.
publish_results: true

# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
# format to the repository Actions tab.
- name: "Upload artifact"
uses: actions/upload-artifact@3cea5372237819ed00197afe530f5a7ea3e805c8 # v3.1.0
with:
name: SARIF file
path: results.sarif
retention-days: 5

# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@17573ee1cc1b9d061760f3a006fc4aac4f944fd5 # v2.2.4
with:
sarif_file: results.sarif
96 changes: 82 additions & 14 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -6,25 +6,44 @@ Thank you for your interest in contributing to the Devfile Registry Operator! We

Before contributing to this repository for the first time, please review our project's [Code of Conduct](https://github.com/devfile/api/blob/main/CODE_OF_CONDUCT.md)

## Certificate of Origin

By contributing to this project you agree to the Developer Certificate of
Origin (DCO). This document was created by the Linux Kernel community and is a
simple statement that you, as a contributor, have the legal right to make the
contribution. See the [DCO](DCO) file for details.

In order to show your agreement with the DCO you should include at the end of the commit message,
the following line:

```console
Signed-off-by: Firstname Lastname <email@email.com>
```

Once you set your user.name and user.email in your git config, you can sign your commit automatically with `git commit -s`.

## How to Contribute

### Issues

If you spot a problem with the devfile registry, [search if an issue already exists](https://github.com/devfile/api/issues). If a related issue doesn't exist, you can open a new issue using a relevant [issue form](https://github.com/devfile/api/issues/new/choose).
If you spot a problem with the **devfile registry operator**, [search if an issue already exists](https://github.com/devfile/api/issues?q=is%3Aissue+is%3Aopen+label%3Aarea%2Fregistry).

You can tag Devfile Registry related issues with the `/area registry` text in your issue.
If a related issue doesn't exist, you can open a new issue using a relevant [issue form](https://github.com/devfile/api/issues/new/choose). You can tag Devfile Registry Operator related issues with the `/area registry` text in your issue.

### Development

#### First Time Setup

1. Install prerequisites: see [Requirements section in README](README.md#requirements).

2. Fork and clone this repository.

3. Open the folder in the IDE of your choice (VS Code with Go extension, or GoLand is recommended)

#### Build and Run the Operator

The Makefile currently supports both Docker and Podman. To run the proper command replace `<engine>` with either `podman` or `docker` depending on your container engine.

1. Log in to an OpenShift or Kubernetes cluster

2. Run `export IMG=<operator-image>` where `<operator-image>` is the image repository to where you would like to push the image (e.g. `quay.io/user/registry-operator:latest`).
@@ -45,12 +64,51 @@ The Makefile currently supports both Docker and Podman. To run the proper comman

By default, http/2 on the webhook server is disabled due to [CVE-2023-44487](https://github.com/advisories/GHSA-qppj-fm5r-hxr3).

If you want to enable http/2 for the webhook server, build with `ENABLE_WEBHOOK_HTTP2=true make docker-build` or with
If you want to enable http/2 for the webhook server, build with `ENABLE_WEBHOOK_HTTP2=true make <engine>-build` or with
`ENABLE_WEBHOOK_HTTP2=true make run` if running locally.

##### Using other platforms

If you need to target another platform for container builds, such as Apple silicon, you can use `TARGET_ARCH=<architecture> make <engine>-build`.

For example, to target container build to `arm64` run the following:

```sh
TARGET_ARCH=arm64 make <engine>-build
```

**Note:** Container builds only use `linux` as the operating system as local cluster runtime environments, such as `minikube` environments, run under Linux virtual machines for other operating systems. For example, _Apple silicon_ would just use the `arm64` container build.

For local builds, you can also set the target operating system:

**Apple silicon**

```sh
export TARGET_OS=darwin
export TARGET_ARCH=arm64
make manager
```

**Linux ARM**

```sh
export TARGET_ARCH=arm64
make manager
```

**Windows**

```sh
export TARGET_OS=windows
export TARGET_ARCH=amd64
make manager
```

By default, `amd64` is used for the target architecture and `linux` is used for the target operating system.

### Testing your Changes

All changes delivered to the Devfile Registry operator are expected to be sufficiently tested. This may include validating that existing tests pass, updating tests, or adding new tests.
All changes delivered to the Devfile Registry Operator are expected to be sufficiently tested. This may include validating that existing tests pass, updating tests, or adding new tests.

#### Unit Tests

@@ -60,32 +118,42 @@ The unit tests for this repository are located under the `pkg/` folder and are d

The integration tests for this repository are located under the `tests/integration` folder and contain tests that validate the Operator's functionality when running on an OpenShift cluster.

To run these tests, run the following commands :
One of the `oc` or `kubectl` executables must be accessible. If both are present in your path, `oc` will be used, except if you
define the environment variable `K8S_CLI` with the command you prefer to use.

```bash
export IMG=<your-built-operator-image>
make test-integration
```
By default, the tests will use the default image for the operator, `quay.io/devfile/registry-operator:next`.

You can use `make <engine>-build` to build your own image, `make <engine>-push` to publish it - Replace `<engine>` with `podman` or `docker`. You will need to have a reference to your newly built image available via the `IMG` environment variable.

<!--
Will need to be updated after the completion of https://github.com/devfile/api/issues/1523
-->

In order for `make test-integration` to properly run you must first ensure your environment is prepared for the operator. The following steps should be done **before** running `make test-integration` and
these commands should be run from the root of the repository where the [`Makefile`](Makefile) is located.

1. Run `make install-cert` and wait for pods in the `cert-manager` namespace to be running before moving to step 2.
2. Run `make install && make deploy` and wait until pods in the `registry-operator-system` namespace are running before proceeding to step 3.
3. Run `make test-integration` or `IMG=<your-operator-image> make test-integration` to run the integration tests.

### Submitting Pull Request

**Note:** All commits must be signed off with the footer:

```
Signed-off-by: First Lastname <email@email.com>
```

You can easily add this footer to your commits by adding `-s` when running `git commit`. When you think the code is ready for review, create a pull request and link the issue associated with it.

Owners of the repository will watch out for and review new PRs.
Owners of the repository will watch out for new PRs and provide reviews to them.

By default for each change in the PR, GitHub Actions and OpenShift CI will run checks against your changes (linting, unit testing, and integration tests)
For each change in the PR, GitHub Actions and OpenShift CI will by default run checks against your changes (linting, unit testing, and integration tests).

If comments have been given in a review, they have to be addressed before merging.

After addressing review comments, don’t forget to add a comment in the PR afterward, so everyone gets notified by Github and knows to re-review.

After addressing review comments, don't forget to add a comment in the PR with the reviewer mentioned afterward, so they get notified by Github to provide a re-review.

# Contact us

If you have questions, please visit us on `#devfile` on the [Kubernetes Slack](https://slack.k8s.io).
If you have any questions, please visit the `#devfile` channel under the [Kubernetes Slack](https://slack.k8s.io) workspace.
37 changes: 37 additions & 0 deletions DCO
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
Developer Certificate of Origin
Version 1.1

Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
1 Letterman Drive
Suite D4700
San Francisco, CA, 94129

Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.


Developer's Certificate of Origin 1.1

By making a contribution to this project, I certify that:

(a) The contribution was created in whole or in part by me and I
have the right to submit it under the open source license
indicated in the file; or

(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or

(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.

(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.
13 changes: 13 additions & 0 deletions DEVFILE_REGISTRY.md
Original file line number Diff line number Diff line change
@@ -206,3 +206,16 @@ spec:
ingressDomain: $INGRESS_DOMAIN
EOF
```
## Accessing the Deployed Registry

After the devfile registry is deployed to the cluster you can access it via the `ingressDomain` you set. If you deployed to Minikube and are currently working on MacOS and you find trying to connect via the `ingressDomain` is timing out, please see [MacOS Troubleshooting](#macos-troubleshooting).

## MacOS Troubleshooting

Currently there is an issue with Minikube and MacOS where you cannot connect to a cluster using an ingress service. If this occurs you can follow these steps to access your cluster:
1. Edit `/etc/hosts` to map `127.0.0.1` to your `ingressDomain`. Typically this is set to your Minikube ip, however, MacOS needs localhost.
2. Open a new terminal and run `minikube tunnel`. This opens a route using the cluster's ip address.
3. In a different terminal than the one you ran `minikube tunnel` in, run `minikube service devfile-registry --url -n <namespace of the registry>`. This will output 3 ips including ports.
4. The ip addresses and ports listed in step 4 are used to access your registry. The ports are the most important here as you can access the registry with `127.0.0.1:<port>` or `<ingressDomain>:<port>`

From testing, majority of the time the first `<ip>:<port>` directs you to `/viewer` so you can view the registry in your browser. If this differs you will need to try them all.
6 changes: 3 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -14,8 +14,8 @@
# limitations under the License.

# Build the manager binary
FROM golang:1.19 as builder
ARG TARGETARCH
FROM golang:1.21@sha256:b405b620c7b53ef64695c7da7c8396f411f381c1eb7da6713c585dd7eca1559b as builder
ARG TARGETARCH=amd64

WORKDIR /workspace
# Copy the Go Modules manifests
@@ -43,7 +43,7 @@ ENV ENABLE_WEBHOOK_HTTP2=${ENABLE_WEBHOOK_HTTP2}

# Use distroless as minimal base image to package the manager binary
# Refer to https://github.com/GoogleContainerTools/distroless for more details
FROM gcr.io/distroless/static:nonroot
FROM gcr.io/distroless/static:nonroot-${TARGETARCH}
WORKDIR /
COPY --from=builder /workspace/manager .
USER 1001
82 changes: 73 additions & 9 deletions Makefile
Original file line number Diff line number Diff line change
@@ -32,6 +32,8 @@ BUNDLE_METADATA_OPTS ?= $(BUNDLE_CHANNELS) $(BUNDLE_DEFAULT_CHANNEL)

# Image URL to use all building/pushing image targets
IMG ?= quay.io/devfile/registry-operator:next
# ENVTEST_VERSION refers to the version of the setup-envtest binary to use
ENVTEST_VERSION=v0.0.0-20240405143037-c25fe2f5ca0f
# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary.
ENVTEST_K8S_VERSION = 1.26
# Controller tools version number
@@ -62,7 +64,6 @@ endif
# operator-sdk
OPERATOR_SDK_CLI ?= operator-sdk


# Setting SHELL to bash allows bash commands to be executed by recipes.
# This is a requirement for 'setup-envtest.sh' in the test target.
# Options are set to exit when a recipe line exits non-zero or a piped command fails.
@@ -101,6 +102,10 @@ KUSTOMIZE ?= $(LOCALBIN)/kustomize
CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen
ENVTEST ?= $(LOCALBIN)/setup-envtest

## Target platform
TARGET_OS ?= linux # `make manager` only
TARGET_ARCH ?= amd64

##@ Development

.PHONY: test
@@ -119,7 +124,7 @@ test-integration:

.PHONY: build manager
manager: manifests generate fmt vet ## Build manager binary.
go build -o $(LOCALBIN)/manager ./main.go
GOOS=${TARGET_OS} GOARCH=${TARGET_ARCH} go build -o $(LOCALBIN)/manager ./main.go

.PHONY: run
run: manifests generate fmt vet ## Run a controller from your host.
@@ -171,7 +176,8 @@ generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and
# Build the docker image
.PHONY: docker-build
docker-build:
docker build . -t ${IMG} --build-arg ENABLE_WEBHOOKS=${ENABLE_WEBHOOKS} \
docker build . -t ${IMG} --build-arg TARGETARCH=${TARGET_ARCH} \
--build-arg ENABLE_WEBHOOKS=${ENABLE_WEBHOOKS} \
--build-arg ENABLE_WEBHOOK_HTTP2=${ENABLE_WEBHOOK_HTTP2}

# Push the docker image
@@ -187,20 +193,78 @@ docker-push:
# To properly provided solutions that supports more than one platform you should use this option.
# **docker-buildx does not work with podman**
PLATFORMS ?= linux/arm64,linux/amd64,linux/s390x,linux/ppc64le
.PHONY: docker-buildx
docker-buildx: test ## Build and push docker image for the manager for cross-platform support
# copy existing Dockerfile and insert --platform=${BUILDPLATFORM} into Dockerfile.cross, and preserve the original Dockerfile

.PHONY: docker-buildx-build
docker-buildx-build: test
$(MAKE) docker-buildx-setup
- docker buildx build --platform=$(PLATFORMS) --tag ${IMG} --provenance=false -f Dockerfile.cross $(shell pwd)
$(MAKE) docker-buildx-cleanup

.PHONY: docker-buildx-push
docker-buildx-push: test
$(MAKE) docker-buildx-setup
- docker buildx build --push --platform=$(PLATFORMS) --tag ${IMG} --provenance=false -f Dockerfile.cross $(shell pwd)
$(MAKE) docker-buildx-cleanup

# INTERNAL: Used to setup the docker-buildx command, not to be called by users
.PHONY: docker-buildx-setup
docker-buildx-setup:
sed -e '1 s/\(^FROM\)/FROM --platform=\$$\{BUILDPLATFORM\}/; t' -e ' 1,// s//FROM --platform=\$$\{BUILDPLATFORM\}/' Dockerfile > Dockerfile.cross
- docker buildx create --name registry-operator-builder
docker buildx use registry-operator-builder
- docker buildx build --push --platform=$(PLATFORMS) --tag ${IMG} -f Dockerfile.cross $(shell pwd)

# INTERNAL: Used to cleanup the docker-buildx command, not to be called by users
.PHONY: docker-buildx-cleanup
docker-buildx-cleanup:
- docker buildx rm registry-operator-builder
rm Dockerfile.cross

# Build the bundle image.
.PHONY: docker-bundle-buildx-build
docker-bundle-buildx-build:
$(MAKE) docker-bundle-buildx-setup
- docker buildx build --platform=${PLATFORMS} --tag ${BUNDLE_IMG} --provenance=false -f bundle.Dockerfile $(shell pwd)
$(MAKE) docker-bundle-buildx-cleanup

.PHONY: docker-bundle-buildx-push
docker-bundle-buildx-push:
$(MAKE) docker-bundle-buildx-setup
- docker buildx build --push --platform=${PLATFORMS} --tag ${BUNDLE_IMG} --provenance=false -f bundle.Dockerfile $(shell pwd)
$(MAKE) docker-bundle-buildx-cleanup

# INTERNAL: Used to setup the docker-bundle-buildx command, not to be called by users
.PHONY: docker-bundle-buildx-setup
docker-bundle-buildx-setup:
- docker buildx create --name bundle-builder
docker buildx use bundle-builder

# INTERNAL: Used to cleanup the docker-bundle-buildx command, not to be called by users
.PHONY: docker-bundle-buildx-cleanup
docker-bundle-buildx-cleanup:
- docker buildx rm bundle-builder

# Clone of docker-buildx command but redesigned to work with podman's workflow
# Designed to build and push multi architecture images of the registry operator
# This utilizes a new Dockerfile to insert platform args to preserve the original Dockerfile
.PHONY: podman-buildx-build
podman-buildx-build: test
sed -e '1 s/\(^FROM\)/FROM --platform=\$$\{BUILDPLATFORM\}/; t' -e ' 1,// s//FROM --platform=\$$\{BUILDPLATFORM\}/' Dockerfile > Dockerfile.cross
- podman manifest create ${IMG}
- podman build --platform=$(PLATFORMS) --manifest ${IMG} -f Dockerfile.cross $(shell pwd)

# Contains cleanup steps to remove files and manifests created during build
.PHONY: podman-buildx-push
podman-buildx-push:
- podman manifest push ${IMG}
- podman manifest rm ${IMG}
rm Dockerfile.cross

# Build the podman image
.PHONY: podman-build
podman-build:
podman build . -t ${IMG} --build-arg ENABLE_WEBHOOKS=${ENABLE_WEBHOOKS}
podman build . -t ${IMG} --build-arg TARGETARCH=${TARGET_ARCH} \
--build-arg ENABLE_WEBHOOKS=${ENABLE_WEBHOOKS} \
--build-arg ENABLE_WEBHOOK_HTTP2=${ENABLE_WEBHOOK_HTTP2}

# Push the podman image
.PHONY: podman-push
@@ -252,7 +316,7 @@ $(KUSTOMIZE): $(LOCALBIN)
.PHONY: envtest
envtest: $(ENVTEST) ## Download envtest-setup locally if necessary.
$(ENVTEST): $(LOCALBIN)
test -s $(LOCALBIN)/setup-envtest || GOBIN=$(LOCALBIN) GOFLAGS="" go install sigs.k8s.io/controller-runtime/tools/setup-envtest@latest
test -s $(LOCALBIN)/setup-envtest || GOBIN=$(LOCALBIN) GOFLAGS="" go install sigs.k8s.io/controller-runtime/tools/setup-envtest@$(ENVTEST_VERSION)

# Generate bundle manifests and metadata, then validate generated files.
.PHONY: bundle
8 changes: 4 additions & 4 deletions OWNERS
Original file line number Diff line number Diff line change
@@ -2,14 +2,14 @@

approvers:
- elsony
- Jdubrick
- michael-valdron
- thepetk
- feloy
- rm3l
- johnmcollier

reviewers:
- elsony
- Jdubrick
- michael-valdron
- thepetk
- feloy
- rm3l
- johnmcollier
67 changes: 51 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
@@ -4,20 +4,32 @@
<div id="header">

[![Apache2.0 License](https://img.shields.io/badge/license-Apache2.0-brightgreen.svg)](LICENSE)
[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/8256/badge)](https://www.bestpractices.dev/projects/8256)
[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/devfile/registry-operator/badge)](https://securityscorecards.dev/viewer/?uri=github.com/devfile/registry-operator)
</div>

The Devfile Registry operator manages the lifecycle of the following custom resources:
1. [Devfile Registry](DEVFILE_REGISTRY.md)
2. [Devfile Registries List](REGISTRIES_LISTS.md)
3. [Cluster Devfile Registries List](REGISTRIES_LISTS.md)

## Releases

**Minor releases** will be scheduled roughly on a _quarterly basis_ and will include non-feature breaking changes made between release cycles.

**Patch releases** are provided as needed for _critical bug fixes_ and _security patches_, it is **strongly recommended** for users running on the same minor version of the registry operator to update when these releases become available.

Releases are available on [GitHub](https://github.com/devfile/registry-operator/releases) along with bundle entries on [OperatorHub.io](https://operatorhub.io) and the OpenShift OperatorHub community catalog.

For more updates on releases, please join our [communication channels](https://devfile.io/docs/2.2.2/community#getting-involved).

## Issue Tracking

Issue tracking repo: https://github.com/devfile/api with label area/registry

## Changelog

Access the [CHANGELOG.md](CHANGELOG.md) here.
This repository utilizes Release Notes to track and display changes, a changelog for every release can be found [here](https://github.com/devfile/registry-operator/releases).

## Requirements

@@ -34,7 +46,7 @@ security setups.

### Development

- Go 1.19.x
- Go 1.21.x
- Docker / Podman
- Operator SDK 1.28.x

@@ -56,14 +68,38 @@ make install && make deploy

The operator will be installed under the `registry-operator-system` namespace. However, devfile registries can be deployed in any namespace.

## Deploying registry to a cluster

After the operator has been installed to a cluster you can deploy a devfile registry by following [these instructions](DEVFILE_REGISTRY.md).
## Development

The repository contains a Makefile; building and deploying can be configured via the environment variables

|variable|purpose|default value|
|---|---|---|
| `IMG` | Image used for controller (run makefile, if `IMG` is updated) | `quay.io/devfile/registry-operator:next` |
| `BUNDLE_IMG` | Image used for bundle OLM package | `quay.io/devfile/registry-operator-bundle:<latest_version>` |
| `CERT_MANAGER_VERSION` | Version of `cert-manager` installed using `make install-cert` | `v1.11.0` |
| `ENABLE_WEBHOOKS` | If `false`, disables operator webhooks | `true` |
| `ENABLE_WEBHOOK_HTTP2` | Overrides webhook HTTP server deployment to use http/2 if set to `true`, **not recommended** | `false` |
| `BUNDLE_CHANNELS` | Sets the list channel(s) include bundle build under | `alpha` |
| `BUNDLE_DEFAULT_CHANNEL` | Sets the default channel to use when installing the bundle | |
| `ENVTEST_K8S_VERSION` | Version of k8s to use for the test environment | `1.26` (current) |
| `CONTROLLER_TOOLS_VERSION` | Version of the controller tools | `v0.9.2` |
| `KUSTOMIZE_VERSION` | Version of kustomize | `v3.8.7` |
| `GOBIN` | Path to install Go binaries to | `${GOPATH}/bin` |
| `K8S_CLI` | Path to CLI tool to use with the target cluster environment, `kubectl` or `oc` | Either `oc` or `kubectl` if installed in that order |
| `OPERATOR_SDK_CLI` | CLI path to `operator-sdk` tool | `operator-sdk` |
| `SHELL` | Active shell to use with make | `/usr/bin/env bash -o pipefail` |
| `LOCALBIN` | Path to place project binaries | `./bin` |
| `KUSTOMIZE` | Path to target `kustomize` binary | `${LOCALBIN}/kustomize` |
| `CONTROLLER_GEN` | Path to target `controller-gen` binary | `${LOCALBIN}/controller-gen` |
| `ENVTEST` | Path to target `setup-envtest` binary | `${LOCALBIN}/setup-envtest` |
| `TARGET_ARCH` | Target architecture for operator manager builds, possible values: `amd64`, `arm64`, `s390x`, `ppc64le` | `amd64` |
| `TARGET_OS` | Target operating system for operator manager build, **only for `make manager`** | `linux` |
| `PLATFORMS` | Target architecture(s) for `make docker-buildx` | All supported: `linux/arm64,linux/amd64,linux/s390x,linux/ppc64le` |
| `KUSTOMIZE_INSTALL_SCRIPT` | URL of kustomize installation script, see [kustomize installation instructions](https://kubectl.docs.kubernetes.io/installation/kustomize/binaries/) | `https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh` |


Some of the rules supported by the makefile:

@@ -73,7 +109,12 @@ Some of the rules supported by the makefile:
| kustomize | install the kustomize tool, used by other commands |
| docker-build | build registry operator container image using docker |
| docker-push | push registry operator container image using docker |
| docker-buildx | build & push registry operator docker image for all supported architectures |
| docker-buildx-build | build registry operator docker image for all supported architectures using docker|
| docker-buildx-push | build & push registry operator docker image for all supported architectures using docker|
| docker-bundle-buildx-build | build registry operator bundle docker image for all supported architectures using docker|
| docker-bundle-buildx-push | build & push registry operator bundle docker image for all supported architectures using docker|
| podman-buildx-build | build registry operator docker image for all supported architectures using podman|
| podman-buildx-push | push registry operator docker image for all supported architectures using podman|
| podman-build | build registry operator container image using podman |
| podman-push | push registry operator container image using podman |
| deploy | deploy operator to cluster |
@@ -96,18 +137,7 @@ To see all rules supported by the makefile, run `make help`

## Testing

To run integration tests for the operator, run `make test-integration`.

One of the `oc` or `kubectl` executables must be accessible. If both are present in your path, `oc` will be used, except if you
define the environment variable `K8S_CLI` with the command you prefer to use.

By default, the tests will use the default image for the operator, `quay.io/devfile/registry-operator:next`.

You can use `make <engine>-build` to build your own image, `make <engine>-push` to publish it - Replace `<engine>` with `podman` or `docker`. Then, to use your own image, run:

```
IMG=<your-operator-image> make test-integration
```
Detailed instructions on how to run tests for the Devfile Registry Operator are found in [CONTRIBUTING.md](CONTRIBUTING.md#testing-your-changes).

### Run operator locally
It's possible to run an instance of the operator locally while communicating with a cluster.
@@ -138,4 +168,9 @@ make run ENABLE_WEBHOOKS=false

## Contributing

Please see our [contributing.md](./CONTRIBUTING.md).
Please see our [CONTRIBUTING.md](./CONTRIBUTING.md).

## Known Issues
- [`make test-integration` times out when running in Minikube](https://github.com/devfile/api/issues/1313)
- [Headless mode field does not update devfile registry state during reconcile](https://github.com/devfile/api/issues/1258)
- [`make bundle` removes `alm-examples` for `DevfileRegistriesList` and `ClusterDevfileRegistriesList` CRDs due to bug with Kustomize](https://github.com/kubernetes-sigs/kustomize/issues/5042)
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.3.0
0.3.0
19 changes: 10 additions & 9 deletions api/v1alpha1/clusterdevfileregistrieslist_webhook.go
Original file line number Diff line number Diff line change
@@ -26,6 +26,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/webhook"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
)

// log is for logging in this package.
@@ -54,7 +55,7 @@ func (r *ClusterDevfileRegistriesList) Default() {
var _ webhook.Validator = &ClusterDevfileRegistriesList{}

// ValidateCreate implements webhook.Validator so a webhook will be registered for the type
func (r *ClusterDevfileRegistriesList) ValidateCreate() error {
func (r *ClusterDevfileRegistriesList) ValidateCreate() (admission.Warnings, error) {
clusterdevfileregistrieslistlog.Info("validate create", "name", r.Name)
//limit CR creation to one per cluster
clusterDevfileRegistriesList := &ClusterDevfileRegistriesListList{}
@@ -63,29 +64,29 @@ func (r *ClusterDevfileRegistriesList) ValidateCreate() error {
}

if err := kubeClient.List(context.TODO(), clusterDevfileRegistriesList, listOpts...); err != nil {
return fmt.Errorf("error listing clusterDevfileRegistriesList custom resources: %v", err)
return nil, fmt.Errorf("error listing clusterDevfileRegistriesList custom resources: %v", err)
}

if len(clusterDevfileRegistriesList.Items) == 1 {
return fmt.Errorf(multiCRError)
return nil, fmt.Errorf(multiCRError)
}

if err := validateURLs(r.Spec.DevfileRegistries); err != nil {
return err
return nil, err
}

return IsNamespaceValid(r.Namespace)
return nil, IsNamespaceValid(r.Namespace)
}

// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
func (r *ClusterDevfileRegistriesList) ValidateUpdate(old runtime.Object) error {
func (r *ClusterDevfileRegistriesList) ValidateUpdate(old runtime.Object) (admission.Warnings, error) {
clusterdevfileregistrieslistlog.Info("validate update", "name", r.Name)
//re-validate the entire list to ensure existing URLs have not gone stale
return validateURLs(r.Spec.DevfileRegistries)
return nil, validateURLs(r.Spec.DevfileRegistries)
}

// ValidateDelete implements webhook.Validator so a webhook will be registered for the type
func (r *ClusterDevfileRegistriesList) ValidateDelete() error {
func (r *ClusterDevfileRegistriesList) ValidateDelete() (admission.Warnings, error) {
clusterdevfileregistrieslistlog.Info("validate delete", "name", r.Name)
return nil
return nil, nil
}
19 changes: 10 additions & 9 deletions api/v1alpha1/devfileregistrieslist_webhook.go
Original file line number Diff line number Diff line change
@@ -25,6 +25,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/webhook"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
)

// log is for logging in this package.
@@ -54,7 +55,7 @@ func (r *DevfileRegistriesList) Default() {
var _ webhook.Validator = &DevfileRegistriesList{}

// ValidateCreate implements webhook.Validator so a webhook will be registered for the type
func (r *DevfileRegistriesList) ValidateCreate() error {
func (r *DevfileRegistriesList) ValidateCreate() (admission.Warnings, error) {
devfileregistrieslistlog.Info("validate create", "name", r.Name)

//limit CR creation to one per namespace
@@ -64,29 +65,29 @@ func (r *DevfileRegistriesList) ValidateCreate() error {
}

if err := kubeClient.List(context.TODO(), devfileRegistriesList, listOpts...); err != nil {
return fmt.Errorf("error listing devfileRegistriesList custom resources: %v", err)
return nil, fmt.Errorf("error listing devfileRegistriesList custom resources: %v", err)
}

if len(devfileRegistriesList.Items) == 1 {
return fmt.Errorf("a DevfileRegistriesList instance already exists. Only one instance can exist on a namespace")
return nil, fmt.Errorf("a DevfileRegistriesList instance already exists. Only one instance can exist on a namespace")
}

if err := validateURLs(r.Spec.DevfileRegistries); err != nil {
return err
return nil, err
}

return IsNamespaceValid(r.Namespace)
return nil, IsNamespaceValid(r.Namespace)
}

// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
func (r *DevfileRegistriesList) ValidateUpdate(old runtime.Object) error {
func (r *DevfileRegistriesList) ValidateUpdate(old runtime.Object) (admission.Warnings, error) {
devfileregistrieslistlog.Info("validate update", "name", r.Name)
//re-validate the entire list to ensure existing URLs have not gone stale
return validateURLs(r.Spec.DevfileRegistries)
return nil, validateURLs(r.Spec.DevfileRegistries)
}

// ValidateDelete implements webhook.Validator so a webhook will be registered for the type
func (r *DevfileRegistriesList) ValidateDelete() error {
func (r *DevfileRegistriesList) ValidateDelete() (admission.Warnings, error) {
devfileregistrieslistlog.Info("validate delete", "name", r.Name)
return nil
return nil, nil
}
19 changes: 19 additions & 0 deletions api/v1alpha1/devfileregistry_types.go
Original file line number Diff line number Diff line change
@@ -70,6 +70,18 @@ type DevfileRegistrySpec struct {
// +operator-sdk:csv:customresourcedefinitions:type=spec
// +optional
Headless *bool `json:"headless,omitempty"`
// Overrides the entire hostname and domain of the devfile registry ingress
// +operator-sdk:csv:customresourcedefinitions:type=spec
// +optional
HostnameOverride string `json:"hostnameOverride,omitempty"`
// Overrides the app name of the devfile registry
// +operator-sdk:csv:customresourcedefinitions:type=spec
// +optional
NameOverride string `json:"nameOverride,omitempty"`
// Overrides the fully qualified app name of the devfile registry
// +operator-sdk:csv:customresourcedefinitions:type=spec
// +optional
FullnameOverride string `json:"fullnameOverride,omitempty"`
}

// DevfileRegistrySpecContainer defines the desired state of a container for the DevfileRegistry
@@ -82,6 +94,10 @@ type DevfileRegistrySpecContainer struct {
// +operator-sdk:csv:customresourcedefinitions:type=spec
// +optional
ImagePullPolicy corev1.PullPolicy `json:"imagePullPolicy,omitempty"`
// Sets the memory limit for the container
// +operator-sdk:csv:customresourcedefinitions:type=spec
// +optional
MemoryLimit string `json:"memoryLimit,omitempty"`
}

// DevfileRegistrySpecStorage defines the desired state of the storage for the DevfileRegistry
@@ -118,6 +134,9 @@ type DevfileRegistrySpecK8sOnly struct {
// Ingress domain for a Kubernetes cluster. This MUST be explicitly specified on Kubernetes. There are no defaults
// +operator-sdk:csv:customresourcedefinitions:type=spec
IngressDomain string `json:"ingressDomain,omitempty"`
// Ingress class for a Kubernetes cluster. Defaults to nginx.
// +operator-sdk:csv:customresourcedefinitions:type=spec
IngressClass string `json:"ingressClass,omitempty"`
}

// Telemetry defines the desired state for telemetry in the DevfileRegistry
13 changes: 7 additions & 6 deletions api/v1alpha1/devfileregistry_webhook.go
Original file line number Diff line number Diff line change
@@ -21,6 +21,7 @@ import (
ctrl "sigs.k8s.io/controller-runtime"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/webhook"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
)

// log is for logging in this package.
@@ -48,19 +49,19 @@ func (r *DevfileRegistry) Default() {
var _ webhook.Validator = &DevfileRegistry{}

// ValidateCreate implements webhook.Validator so a webhook will be registered for the type
func (r *DevfileRegistry) ValidateCreate() error {
func (r *DevfileRegistry) ValidateCreate() (admission.Warnings, error) {
devfileregistrylog.Info("validate create", "name", r.Name)
return IsNamespaceValid(r.Namespace)
return nil, IsNamespaceValid(r.Namespace)
}

// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
func (r *DevfileRegistry) ValidateUpdate(old runtime.Object) error {
func (r *DevfileRegistry) ValidateUpdate(old runtime.Object) (admission.Warnings, error) {
devfileregistrylog.Info("validate update", "name", r.Name)
return nil
return nil, nil
}

// ValidateDelete implements webhook.Validator so a webhook will be registered for the type
func (r *DevfileRegistry) ValidateDelete() error {
func (r *DevfileRegistry) ValidateDelete() (admission.Warnings, error) {
devfileregistrylog.Info("validate delete", "name", r.Name)
return nil
return nil, nil
}
18 changes: 12 additions & 6 deletions api/v1alpha1/webhook_suite_test.go
Original file line number Diff line number Diff line change
@@ -42,6 +42,8 @@ import (
"sigs.k8s.io/controller-runtime/pkg/envtest"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
"sigs.k8s.io/controller-runtime/pkg/webhook"
)

// These tests use Ginkgo (BDD-style Go testing framework). Refer to
@@ -119,12 +121,16 @@ var _ = BeforeSuite(func() {
// start webhook server using Manager
webhookInstallOptions := &testEnv.WebhookInstallOptions
mgr, err := ctrl.NewManager(cfg, ctrl.Options{
Scheme: scheme,
Host: webhookInstallOptions.LocalServingHost,
Port: webhookInstallOptions.LocalServingPort,
CertDir: webhookInstallOptions.LocalServingCertDir,
LeaderElection: false,
MetricsBindAddress: "0",
Scheme: scheme,
WebhookServer: webhook.NewServer(webhook.Options{
Host: webhookInstallOptions.LocalServingHost,
Port: webhookInstallOptions.LocalServingPort,
CertDir: webhookInstallOptions.LocalServingCertDir,
}),
Metrics: metricsserver.Options{
BindAddress: "0",
},
LeaderElection: false,
})
Expect(err).NotTo(HaveOccurred())

2 changes: 1 addition & 1 deletion bundle.Dockerfile
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@ LABEL operators.operatorframework.io.bundle.mediatype.v1=registry+v1
LABEL operators.operatorframework.io.bundle.manifests.v1=manifests/
LABEL operators.operatorframework.io.bundle.metadata.v1=metadata/
LABEL operators.operatorframework.io.bundle.package.v1=registry-operator
LABEL operators.operatorframework.io.bundle.channels.v1=alpha
LABEL operators.operatorframework.io.bundle.channels.v1=beta
LABEL operators.operatorframework.io.metrics.builder=operator-sdk-v1.36.0
LABEL operators.operatorframework.io.metrics.mediatype.v1=metrics+v1
LABEL operators.operatorframework.io.metrics.project_layout=go.kubebuilder.io/v3
30 changes: 27 additions & 3 deletions bundle/manifests/registry-operator.clusterserviceversion.yaml
Original file line number Diff line number Diff line change
@@ -50,7 +50,7 @@ metadata:
capabilities: Basic Install
categories: OpenShift Optional,Developer Tools
containerImage: quay.io/devfile/registry-operator:v0.3.0
createdAt: "2024-08-13T13:58:24Z"
createdAt: "2024-08-20T09:11:27Z"
description: Deploy and manage Devfile Registries on Kubernetes and OpenShift
with the Devfile Registry operator.
operators.operatorframework.io/builder: operator-sdk-v1.36.0
@@ -116,19 +116,35 @@ spec:
- description: Sets the image pull policy for the container
displayName: Image Pull Policy
path: devfileIndex.imagePullPolicy
- description: Sets the memory limit for the container
displayName: Memory Limit
path: devfileIndex.memoryLimit
- description: Sets the container image containing devfile stacks to be deployed
on the Devfile Registry
displayName: Devfile Index Image
path: devfileIndexImage
- description: Overrides the fully qualified app name of the devfile registry
displayName: Fullname Override
path: fullnameOverride
- description: Sets the registry server deployment to run under headless mode
displayName: Headless
path: headless
- description: Overrides the entire hostname and domain of the devfile registry
ingress
displayName: Hostname Override
path: hostnameOverride
- displayName: K8s
path: k8s
- description: Ingress class for a Kubernetes cluster. Defaults to nginx.
displayName: Ingress Class
path: k8s.ingressClass
- description: Ingress domain for a Kubernetes cluster. This MUST be explicitly
specified on Kubernetes. There are no defaults
displayName: Ingress Domain
path: k8s.ingressDomain
- description: Overrides the app name of the devfile registry
displayName: Name Override
path: nameOverride
- description: Sets the OCI registry container spec to be deployed on the Devfile
Registry
displayName: Oci Registry
@@ -139,6 +155,9 @@ spec:
- description: Sets the image pull policy for the container
displayName: Image Pull Policy
path: ociRegistry.imagePullPolicy
- description: Sets the memory limit for the container
displayName: Memory Limit
path: ociRegistry.memoryLimit
- description: |-
Overrides the container image used for the OCI registry.
Recommended to leave blank and default to the image specified by the operator.
@@ -154,6 +173,9 @@ spec:
- description: Sets the image pull policy for the container
displayName: Image Pull Policy
path: registryViewer.imagePullPolicy
- description: Sets the memory limit for the container
displayName: Memory Limit
path: registryViewer.memoryLimit
- description: Overrides the container image used for the registry viewer.
displayName: Registry Viewer Image
path: registryViewerImage
@@ -562,8 +584,10 @@ spec:
name: Michael Valdron
- email: jcollier@redhat.com
name: John Collier
- email: ktsao@redhat.com
name: Kim Tsao
- email: tpetkos@redhat.com
name: Theofanis Petkos
- email: jdubrick@redhat.com
name: Jordan Dubrick
maturity: beta
minKubeVersion: 1.25.0
provider:
24 changes: 24 additions & 0 deletions bundle/manifests/registry.devfile.io_devfileregistries.yaml
Original file line number Diff line number Diff line change
@@ -55,24 +55,42 @@ spec:
imagePullPolicy:
description: Sets the image pull policy for the container
type: string
memoryLimit:
description: Sets the memory limit for the container
type: string
type: object
devfileIndexImage:
description: Sets the container image containing devfile stacks to
be deployed on the Devfile Registry
type: string
fullnameOverride:
description: Overrides the fully qualified app name of the devfile
registry
type: string
headless:
description: Sets the registry server deployment to run under headless
mode
type: boolean
hostnameOverride:
description: Overrides the entire hostname and domain of the devfile
registry ingress
type: string
k8s:
description: DevfileRegistrySpecK8sOnly defines the desired state
of the kubernetes-only fields of the DevfileRegistry
properties:
ingressClass:
description: Ingress class for a Kubernetes cluster. Defaults
to nginx.
type: string
ingressDomain:
description: Ingress domain for a Kubernetes cluster. This MUST
be explicitly specified on Kubernetes. There are no defaults
type: string
type: object
nameOverride:
description: Overrides the app name of the devfile registry
type: string
ociRegistry:
description: Sets the OCI registry container spec to be deployed on
the Devfile Registry
@@ -83,6 +101,9 @@ spec:
imagePullPolicy:
description: Sets the image pull policy for the container
type: string
memoryLimit:
description: Sets the memory limit for the container
type: string
type: object
ociRegistryImage:
description: Overrides the container image used for the OCI registry.
@@ -99,6 +120,9 @@ spec:
imagePullPolicy:
description: Sets the image pull policy for the container
type: string
memoryLimit:
description: Sets the memory limit for the container
type: string
type: object
registryViewerImage:
description: Overrides the container image used for the registry viewer.
2 changes: 1 addition & 1 deletion bundle/metadata/annotations.yaml
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@ annotations:
operators.operatorframework.io.bundle.manifests.v1: manifests/
operators.operatorframework.io.bundle.metadata.v1: metadata/
operators.operatorframework.io.bundle.package.v1: registry-operator
operators.operatorframework.io.bundle.channels.v1: alpha
operators.operatorframework.io.bundle.channels.v1: beta
operators.operatorframework.io.metrics.builder: operator-sdk-v1.36.0
operators.operatorframework.io.metrics.mediatype.v1: metrics+v1
operators.operatorframework.io.metrics.project_layout: go.kubebuilder.io/v3
24 changes: 24 additions & 0 deletions config/crd/bases/registry.devfile.io_devfileregistries.yaml
Original file line number Diff line number Diff line change
@@ -56,24 +56,42 @@ spec:
imagePullPolicy:
description: Sets the image pull policy for the container
type: string
memoryLimit:
description: Sets the memory limit for the container
type: string
type: object
devfileIndexImage:
description: Sets the container image containing devfile stacks to
be deployed on the Devfile Registry
type: string
fullnameOverride:
description: Overrides the fully qualified app name of the devfile
registry
type: string
headless:
description: Sets the registry server deployment to run under headless
mode
type: boolean
hostnameOverride:
description: Overrides the entire hostname and domain of the devfile
registry ingress
type: string
k8s:
description: DevfileRegistrySpecK8sOnly defines the desired state
of the kubernetes-only fields of the DevfileRegistry
properties:
ingressClass:
description: Ingress class for a Kubernetes cluster. Defaults
to nginx.
type: string
ingressDomain:
description: Ingress domain for a Kubernetes cluster. This MUST
be explicitly specified on Kubernetes. There are no defaults
type: string
type: object
nameOverride:
description: Overrides the app name of the devfile registry
type: string
ociRegistry:
description: Sets the OCI registry container spec to be deployed on
the Devfile Registry
@@ -84,6 +102,9 @@ spec:
imagePullPolicy:
description: Sets the image pull policy for the container
type: string
memoryLimit:
description: Sets the memory limit for the container
type: string
type: object
ociRegistryImage:
description: Overrides the container image used for the OCI registry.
@@ -100,6 +121,9 @@ spec:
imagePullPolicy:
description: Sets the image pull policy for the container
type: string
memoryLimit:
description: Sets the memory limit for the container
type: string
type: object
registryViewerImage:
description: Overrides the container image used for the registry viewer.
Original file line number Diff line number Diff line change
@@ -114,19 +114,35 @@ spec:
- description: Sets the image pull policy for the container
displayName: Image Pull Policy
path: devfileIndex.imagePullPolicy
- description: Sets the memory limit for the container
displayName: Memory Limit
path: devfileIndex.memoryLimit
- description: Sets the container image containing devfile stacks to be deployed
on the Devfile Registry
displayName: Devfile Index Image
path: devfileIndexImage
- description: Overrides the fully qualified app name of the devfile registry
displayName: Fullname Override
path: fullnameOverride
- description: Sets the registry server deployment to run under headless mode
displayName: Headless
path: headless
- description: Overrides the entire hostname and domain of the devfile registry
ingress
displayName: Hostname Override
path: hostnameOverride
- displayName: K8s
path: k8s
- description: Ingress class for a Kubernetes cluster. Defaults to nginx.
displayName: Ingress Class
path: k8s.ingressClass
- description: Ingress domain for a Kubernetes cluster. This MUST be explicitly
specified on Kubernetes. There are no defaults
displayName: Ingress Domain
path: k8s.ingressDomain
- description: Overrides the app name of the devfile registry
displayName: Name Override
path: nameOverride
- description: Sets the OCI registry container spec to be deployed on the Devfile
Registry
displayName: Oci Registry
@@ -137,6 +153,9 @@ spec:
- description: Sets the image pull policy for the container
displayName: Image Pull Policy
path: ociRegistry.imagePullPolicy
- description: Sets the memory limit for the container
displayName: Memory Limit
path: ociRegistry.memoryLimit
- description: |-
Overrides the container image used for the OCI registry.
Recommended to leave blank and default to the image specified by the operator.
@@ -152,6 +171,9 @@ spec:
- description: Sets the image pull policy for the container
displayName: Image Pull Policy
path: registryViewer.imagePullPolicy
- description: Sets the memory limit for the container
displayName: Memory Limit
path: registryViewer.memoryLimit
- description: Overrides the container image used for the registry viewer.
displayName: Registry Viewer Image
path: registryViewerImage
@@ -282,8 +304,10 @@ spec:
name: Michael Valdron
- email: jcollier@redhat.com
name: John Collier
- email: ktsao@redhat.com
name: Kim Tsao
- email: tpetkos@redhat.com
name: Theofanis Petkos
- email: jdubrick@redhat.com
name: Jordan Dubrick
maturity: beta
minKubeVersion: 1.25.0
provider:
1 change: 1 addition & 0 deletions controllers/condition_types.go
Original file line number Diff line number Diff line change
@@ -20,4 +20,5 @@ const (
typeValidateDevfileRegistries = "ValidateDevfileRegistries"
typeUpdateDevfileRegistries = "UpdateDevfileRegistries"
typeUpdateDevfileRegistry = "UpdateDevfileRegistry"
typeNoDeployDevfileRegistry = "NoDeployDevfileRegistry"
)
20 changes: 18 additions & 2 deletions controllers/devfileregistry_controller.go
Original file line number Diff line number Diff line change
@@ -75,6 +75,22 @@ func (r *DevfileRegistryReconciler) Reconcile(ctx context.Context, req ctrl.Requ
return ctrl.Result{}, err
}

// Block the Devfile Registry deployment if an Ingress domain is missing for Kubernetes
if !config.ControllerCfg.IsOpenShift() && registry.IsIngressSkipped(devfileRegistry) {
meta.SetStatusCondition(&devfileRegistry.Status.Conditions, metav1.Condition{
Type: typeNoDeployDevfileRegistry,
Status: metav1.ConditionUnknown,
Reason: "DeploymentBlocked",
Message: "No Ingress domain set for Devfile Registry - Deployment Blocked",
})

log.Info("Blocked deployment due to unset Ingress domain")

err = r.Status().Update(ctx, devfileRegistry)

return ctrl.Result{}, err
}

if devfileRegistry.Status.Conditions == nil || len(devfileRegistry.Status.Conditions) == 0 {
err = retry.RetryOnConflict(retry.DefaultRetry, func() error {
meta.SetStatusCondition(&devfileRegistry.Status.Conditions, metav1.Condition{
@@ -94,7 +110,7 @@ func (r *DevfileRegistryReconciler) Reconcile(ctx context.Context, req ctrl.Requ
}

// Generate labels for any subresources generated by the operator
labels := registry.LabelsForDevfileRegistry(devfileRegistry.Name)
labels := registry.LabelsForDevfileRegistry(devfileRegistry)

log.Info("Deploying registry")

@@ -141,7 +157,7 @@ func (r *DevfileRegistryReconciler) Reconcile(ctx context.Context, req ctrl.Requ

// Get the hostname of the generated devfile route
devfilesRoute := &routev1.Route{}
err = r.Get(ctx, types.NamespacedName{Name: registry.IngressName(devfileRegistry.Name), Namespace: devfileRegistry.Namespace}, devfilesRoute)
err = r.Get(ctx, types.NamespacedName{Name: registry.IngressName(devfileRegistry), Namespace: devfileRegistry.Namespace}, devfilesRoute)
if err != nil {
// Log an error, but requeue, as the controller's cached kube client likely hasn't registered the new route yet.
// See https://github.com/operator-framework/operator-sdk/issues/4013#issuecomment-707267616 for an explanation on why we requeue rather than error out here
16 changes: 8 additions & 8 deletions controllers/ensure.go
Original file line number Diff line number Diff line change
@@ -36,7 +36,7 @@ import (

func (r *DevfileRegistryReconciler) ensure(ctx context.Context, cr *registryv1alpha1.DevfileRegistry, resource client.Object, labels map[string]string, ingressDomain string) (*reconcile.Result, error) {
resourceType := reflect.TypeOf(resource).Elem().Name()
resourceName := getResourceName(resource, cr.Name)
resourceName := getResourceName(resource, cr)
//use the controller log
// Check to see if the requested resource exists on the cluster. If it doesn't exist, create it and return.
err := r.Get(ctx, types.NamespacedName{Name: resourceName, Namespace: cr.Namespace}, resource)
@@ -74,20 +74,20 @@ func (r *DevfileRegistryReconciler) ensure(ctx context.Context, cr *registryv1al
return nil, nil
}

func getResourceName(resource runtime.Object, crName string) string {
func getResourceName(resource runtime.Object, cr *registryv1alpha1.DevfileRegistry) string {
switch resource.(type) {
case *appsv1.Deployment:
return registry.DeploymentName(crName)
return registry.DeploymentName(cr)
case *corev1.ConfigMap:
return registry.ConfigMapName(crName)
return registry.ConfigMapName(cr)
case *corev1.PersistentVolumeClaim:
return registry.PVCName(crName)
return registry.PVCName(cr)
case *corev1.Service:
return registry.ServiceName(crName)
return registry.ServiceName(cr)
case *routev1.Route, *networkingv1.Ingress:
return registry.IngressName(crName)
return registry.IngressName(cr)
}
return registry.GenericResourceName(crName)
return registry.GenericResourceName(cr)
}

func (r *DevfileRegistryReconciler) generateResourceObject(cr *registryv1alpha1.DevfileRegistry, resource client.Object, labels map[string]string, ingressDomain string) client.Object {
2 changes: 1 addition & 1 deletion controllers/update.go
Original file line number Diff line number Diff line change
@@ -197,7 +197,7 @@ func (r *DevfileRegistryReconciler) deleteOldPVCIfNeeded(ctx context.Context, cr
// Check to see if a PVC exists, if so, need to clean it up because storage was disabled
if !registry.IsStorageEnabled(cr) {
pvc := &corev1.PersistentVolumeClaim{}
err := r.Get(ctx, types.NamespacedName{Name: registry.PVCName(cr.Name), Namespace: cr.Namespace}, pvc)
err := r.Get(ctx, types.NamespacedName{Name: registry.PVCName(cr), Namespace: cr.Namespace}, pvc)
if err != nil {
if errors.IsNotFound(err) {
// PVC not found, so there's no old PVC to delete. Just return nil, nothing to do.
137 changes: 71 additions & 66 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,103 +1,108 @@
module github.com/devfile/registry-operator

go 1.19
go 1.21

require (
github.com/devfile/registry-support/index/generator v0.0.0-20221018203505-df96d34d4273
github.com/devfile/registry-support/registry-library v0.0.0-20230327144043-0f64fa10dd3d
github.com/go-logr/logr v1.2.3
github.com/devfile/registry-support/index/generator v0.0.0-20240816133831-cf509ccd1a6b
github.com/devfile/registry-support/registry-library v0.0.0-20240816160225-30dce468d0c0
github.com/go-logr/logr v1.4.1
github.com/hashicorp/go-multierror v1.1.1
github.com/onsi/ginkgo/v2 v2.9.1
github.com/onsi/gomega v1.27.4
github.com/onsi/ginkgo/v2 v2.14.0
github.com/onsi/gomega v1.30.0
github.com/openshift/api v0.0.0-20221013123532-e8b83ffadbab
github.com/stretchr/testify v1.8.1
github.com/stretchr/testify v1.8.4
gopkg.in/yaml.v2 v2.4.0
k8s.io/api v0.26.10
k8s.io/apiextensions-apiserver v0.26.10
k8s.io/apimachinery v0.27.7
k8s.io/client-go v0.26.10
sigs.k8s.io/controller-runtime v0.14.7
k8s.io/api v0.29.2
k8s.io/apiextensions-apiserver v0.29.2
k8s.io/apimachinery v0.29.2
k8s.io/client-go v0.29.2
sigs.k8s.io/controller-runtime v0.17.5
)

require (
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect
github.com/Microsoft/hcsshim v0.11.4 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/containerd/containerd v1.6.12 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/docker/cli v20.10.21+incompatible // indirect
github.com/docker/distribution v2.8.1+incompatible // indirect
github.com/docker/docker v20.10.21+incompatible // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/containerd/containerd v1.7.13 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/distribution/reference v0.5.0 // indirect
github.com/docker/cli v25.0.1+incompatible // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/docker/docker v25.0.6+incompatible // indirect
github.com/docker/docker-credential-helpers v0.7.0 // indirect
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-metrics v0.0.1 // indirect
github.com/docker/go-units v0.4.0 // indirect
github.com/emicklei/go-restful/v3 v3.9.0 // indirect
github.com/evanphx/json-patch/v5 v5.6.0 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/go-logr/zapr v1.2.3 // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/evanphx/json-patch/v5 v5.8.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-logr/zapr v1.3.0 // indirect
github.com/go-openapi/jsonpointer v0.19.6 // indirect
github.com/go-openapi/jsonreference v0.20.1 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-openapi/swag v0.22.3 // indirect
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/gnostic v0.5.7-v3refs // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/gnostic-models v0.6.8 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/mux v1.8.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-version v1.4.0 // indirect
github.com/imdario/mergo v0.3.12 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.13.6 // indirect
github.com/klauspost/compress v1.17.7 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
github.com/moby/locker v1.0.1 // indirect
github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0-rc2 // indirect
github.com/opencontainers/image-spec v1.1.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.14.0 // indirect
github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.39.0 // indirect
github.com/prometheus/procfs v0.8.0 // indirect
github.com/rogpeppe/go-internal v1.11.0 // indirect
github.com/sirupsen/logrus v1.9.0 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_golang v1.18.0 // indirect
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.45.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/spf13/pflag v1.0.5 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.24.0 // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/oauth2 v0.3.0 // indirect
golang.org/x/sync v0.1.0 // indirect
golang.org/x/sys v0.13.0 // indirect
golang.org/x/term v0.13.0 // indirect
golang.org/x/text v0.13.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.7.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 // indirect
google.golang.org/grpc v1.49.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
go.opentelemetry.io/otel v1.24.0 // indirect
go.opentelemetry.io/otel/metric v1.24.0 // indirect
go.opentelemetry.io/otel/trace v1.24.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.26.0 // indirect
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect
golang.org/x/net v0.23.0 // indirect
golang.org/x/oauth2 v0.16.0 // indirect
golang.org/x/sync v0.6.0 // indirect
golang.org/x/sys v0.18.0 // indirect
golang.org/x/term v0.18.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/tools v0.19.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240304212257-790db918fca8 // indirect
google.golang.org/grpc v1.62.1 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/component-base v0.26.10 // indirect
k8s.io/klog/v2 v2.90.1 // indirect
k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect
k8s.io/utils v0.0.0-20230209194617-a36077c30491 // indirect
oras.land/oras-go v1.2.2 // indirect
k8s.io/component-base v0.29.2 // indirect
k8s.io/klog/v2 v2.110.1 // indirect
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect
k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect
oras.land/oras-go v1.2.5 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect
)
1,396 changes: 178 additions & 1,218 deletions go.sum

Large diffs are not rendered by default.

20 changes: 13 additions & 7 deletions main.go
Original file line number Diff line number Diff line change
@@ -22,14 +22,15 @@ import (
"os"

routev1 "github.com/openshift/api/route/v1"
"sigs.k8s.io/controller-runtime/pkg/healthz"

"k8s.io/apimachinery/pkg/runtime"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/healthz"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
"sigs.k8s.io/controller-runtime/pkg/webhook"

registryv1alpha1 "github.com/devfile/registry-operator/api/v1alpha1"
"github.com/devfile/registry-operator/controllers"
@@ -67,12 +68,17 @@ func main() {
ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))

mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
LeaderElection: enableLeaderElection,
Scheme: scheme,
MetricsBindAddress: metricsAddr,
Port: 9443,
HealthProbeBindAddress: probeAddr,
LeaderElection: enableLeaderElection,
LeaderElectionID: "1984829e.devfile.io",
WebhookServer: webhook.NewServer(webhook.Options{
Port: 9443,
TLSOpts: []func(*tls.Config){},
}),
Metrics: metricsserver.Options{
BindAddress: metricsAddr,
},
})
if err != nil {
setupLog.Error(err, "unable to start manager")
@@ -127,8 +133,8 @@ func main() {

if enableWebhookHTTP2 == "false" {
setupLog.Info("disabling http/2 on the webhook server")
server := mgr.GetWebhookServer()
server.TLSOpts = append(server.TLSOpts,
webhookServer := mgr.GetWebhookServer().(*webhook.DefaultServer)
webhookServer.Options.TLSOpts = append(webhookServer.Options.TLSOpts,
func(c *tls.Config) {
c.NextProtos = []string{"http/1.1"}
},
2 changes: 1 addition & 1 deletion pkg/registry/configmap.go
Original file line number Diff line number Diff line change
@@ -59,7 +59,7 @@ DEVFILE_REGISTRIES=[{"name":"%s","url":"http://localhost:8080","fqdn":"%s"}]`,
configMapData[".env.registry-viewer"] = viewerEnvfile

cm := &corev1.ConfigMap{
ObjectMeta: generateObjectMeta(ConfigMapName(cr.Name), cr.Namespace, labels),
ObjectMeta: generateObjectMeta(ConfigMapName(cr), cr.Namespace, labels),
Data: configMapData,
}

21 changes: 21 additions & 0 deletions pkg/registry/constants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//
//
// Copyright Red Hat
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package registry

const maxTruncLength = 63

const localHostname = "localhost:8080"
142 changes: 141 additions & 1 deletion pkg/registry/defaults.go
Original file line number Diff line number Diff line change
@@ -17,8 +17,12 @@
package registry

import (
"fmt"
"strings"

registryv1alpha1 "github.com/devfile/registry-operator/api/v1alpha1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
)

const (
@@ -32,6 +36,11 @@ const (
DefaultRegistryViewerImagePullPolicy = corev1.PullAlways
DefaultOCIRegistryImagePullPolicy = corev1.PullAlways

// Default memory limits
DefaultDevfileIndexMemoryLimit = "256Mi"
DefaultRegistryViewerMemoryLimit = "256Mi"
DefaultOCIRegistryMemoryLimit = "256Mi"

// Defaults/constants for devfile registry storages
DefaultDevfileRegistryVolumeSize = "1Gi"
DevfileRegistryVolumeEnabled = false
@@ -51,6 +60,17 @@ const (
OCIMetricsPort = 5001
OCIServerPort = 5000
RegistryViewerPort = 3000

// Default kubernetes-only fields
DefaultK8sIngressClass = "nginx"

// Override defaults (should be empty)
DefaultHostnameOverride = ""
DefaultNameOverride = ""
DefaultFullnameOverride = ""

// App name default
DefaultAppName = "devfile-registry"
)

// GetRegistryViewerImage returns the container image for the registry viewer to be deployed on the Devfile Registry.
@@ -73,6 +93,13 @@ func GetRegistryViewerImagePullPolicy(cr *registryv1alpha1.DevfileRegistry) core
return DefaultRegistryViewerImagePullPolicy
}

// GetRegistryViewerMemoryLimit returns the memory limit for the registry viewer container.
// In case of invalid quantity given, it returns the default value.
// Default: resource.Quantity{s: "256Mi"}
func GetRegistryViewerMemoryLimit(cr *registryv1alpha1.DevfileRegistry) resource.Quantity {
return getDevfileRegistrySpecContainer(cr.Spec.RegistryViewer.MemoryLimit, DefaultRegistryViewerMemoryLimit)
}

// GetOCIRegistryImage returns the container image for the OCI registry to be deployed on the Devfile Registry.
// Default: "quay.io/devfile/oci-registry:next"
func GetOCIRegistryImage(cr *registryv1alpha1.DevfileRegistry) string {
@@ -93,6 +120,13 @@ func GetOCIRegistryImagePullPolicy(cr *registryv1alpha1.DevfileRegistry) corev1.
return DefaultOCIRegistryImagePullPolicy
}

// GetOCIRegistryMemoryLimit returns the memory limit for the OCI registry container.
// In case of invalid quantity given, it returns the default value.
// Default: resource.Quantity{s: "256Mi"}
func GetOCIRegistryMemoryLimit(cr *registryv1alpha1.DevfileRegistry) resource.Quantity {
return getDevfileRegistrySpecContainer(cr.Spec.OciRegistry.MemoryLimit, DefaultOCIRegistryMemoryLimit)
}

// GetDevfileIndexImage returns the container image for the devfile index server to be deployed on the Devfile Registry.
// Default: "quay.io/devfile/devfile-index:next"
func GetDevfileIndexImage(cr *registryv1alpha1.DevfileRegistry) string {
@@ -113,6 +147,13 @@ func GetDevfileIndexImagePullPolicy(cr *registryv1alpha1.DevfileRegistry) corev1
return DefaultDevfileIndexImagePullPolicy
}

// GetDevfileIndexMemoryLimit returns the memory limit for the devfile index container.
// In case of invalid quantity given, it returns the default value.
// Default: resource.Quantity{s: "256Mi"}
func GetDevfileIndexMemoryLimit(cr *registryv1alpha1.DevfileRegistry) resource.Quantity {
return getDevfileRegistrySpecContainer(cr.Spec.DevfileIndex.MemoryLimit, DefaultDevfileIndexMemoryLimit)
}

func getDevfileRegistryVolumeSize(cr *registryv1alpha1.DevfileRegistry) string {
if cr.Spec.Storage.RegistryVolumeSize != "" {
return cr.Spec.Storage.RegistryVolumeSize
@@ -124,14 +165,53 @@ func GetDevfileRegistryVolumeSource(cr *registryv1alpha1.DevfileRegistry) corev1
if IsStorageEnabled(cr) {
return corev1.VolumeSource{
PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
ClaimName: PVCName(cr.Name),
ClaimName: PVCName(cr),
},
}
}
// If persistence is not enabled, return an empty dir volume source
return corev1.VolumeSource{}
}

// GetK8sIngressClass returns ingress class used for the k8s ingress class field.
// Default: "nginx"
func GetK8sIngressClass(cr *registryv1alpha1.DevfileRegistry) string {
if cr.Spec.K8s.IngressClass != "" {
return cr.Spec.K8s.IngressClass
}
return DefaultK8sIngressClass
}

// GetHostnameOverride returns hostname override used to override the hostname and domain of a devfile registry
// Default: ""
func GetHostnameOverride(cr *registryv1alpha1.DevfileRegistry) string {
if cr.Spec.HostnameOverride != "" {
return cr.Spec.HostnameOverride
}

return DefaultHostnameOverride
}

// GetNameOverride returns name override used to override the app name of a devfile registry
// Default: ""
func GetNameOverride(cr *registryv1alpha1.DevfileRegistry) string {
if cr.Spec.NameOverride != "" {
return cr.Spec.NameOverride
}

return DefaultNameOverride
}

// GetFullnameOverride returns full name override used to override the fully qualified app name of a devfile registry
// Default: ""
func GetFullnameOverride(cr *registryv1alpha1.DevfileRegistry) string {
if cr.Spec.FullnameOverride != "" {
return cr.Spec.FullnameOverride
}

return DefaultFullnameOverride
}

// IsStorageEnabled returns true if storage.enabled is set in the DevfileRegistry CR
// If it's not set, it returns false by default.
func IsStorageEnabled(cr *registryv1alpha1.DevfileRegistry) bool {
@@ -167,3 +247,63 @@ func IsHeadlessEnabled(cr *registryv1alpha1.DevfileRegistry) bool {
}
return DefaultDevfileRegistryHeadlessEnabled
}

func getDevfileRegistrySpecContainer(quantity string, defaultValue string) resource.Quantity {
if quantity != "" {
resourceQuantity, err := resource.ParseQuantity(quantity)
if err == nil {
return resourceQuantity
}
}
return resource.MustParse(defaultValue)
}

// getAppName returns app name of a devfile registry
// truncated to 63 characters max, if `DevfileRegistry.NameOverride`
// is set it will return the override name truncated to 63 characters max
func getAppName(cr *registryv1alpha1.DevfileRegistry) string {
if cr != nil {
nameOverride := GetNameOverride(cr)

if nameOverride == DefaultNameOverride {
return truncateName(DefaultAppName)
}

return truncateName(nameOverride)
}

return truncateName(DefaultAppName)
}

// getAppFullName returns fully qualified app name of a devfile registry
// truncated to 63 characters max, if `DevfileRegistry.FullnameOverride`
// is set it will return the override name truncated to 63 characters max
func getAppFullName(cr *registryv1alpha1.DevfileRegistry) string {
if cr != nil {
fullNameOverride := GetFullnameOverride(cr)

if fullNameOverride == DefaultFullnameOverride {
appName := getAppName(cr)
if cr.Name == "" {
return truncateName(appName)
} else if strings.Contains(cr.Name, appName) {
return truncateName(cr.Name)
} else {
return truncateName(fmt.Sprintf("%s-%s", cr.Name, appName))
}
}

return truncateName(fullNameOverride)
}

return truncateName(DefaultAppName)
}

// IsIngressSkipped returns true if no Ingress is set in the DevfileRegistry CR.
// If cr does not exist return true by default as no Ingress resource should be created
func IsIngressSkipped(cr *registryv1alpha1.DevfileRegistry) bool {
if cr != nil {
return cr.Spec.K8s.IngressDomain == ""
}
return true
}
520 changes: 497 additions & 23 deletions pkg/registry/defaults_test.go

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions pkg/registry/deployment.go
Original file line number Diff line number Diff line change
@@ -39,7 +39,7 @@ func GenerateDeployment(cr *registryv1alpha1.DevfileRegistry, scheme *runtime.Sc
fsGroup := int64(3001)

dep := &appsv1.Deployment{
ObjectMeta: generateObjectMeta(cr.Name, cr.Namespace, labels),
ObjectMeta: generateObjectMeta(DeploymentName(cr), cr.Namespace, labels),
Spec: appsv1.DeploymentSpec{
Replicas: &replicas,
Selector: &metav1.LabelSelector{
@@ -184,7 +184,7 @@ func GenerateDeployment(cr *registryv1alpha1.DevfileRegistry, scheme *runtime.Sc
VolumeSource: corev1.VolumeSource{
ConfigMap: &corev1.ConfigMapVolumeSource{
LocalObjectReference: corev1.LocalObjectReference{
Name: ConfigMapName(cr.Name),
Name: ConfigMapName(cr),
},
Items: []corev1.KeyToPath{
{
@@ -274,10 +274,10 @@ func GenerateDeployment(cr *registryv1alpha1.DevfileRegistry, scheme *runtime.Sc
[
{
"name": "%s",
"url": "http://localhost:8080",
"url": "http://%s",
"fqdn": "%s"
}
]`, cr.ObjectMeta.Name, cr.Status.URL),
]`, cr.ObjectMeta.Name, localHostname, cr.Status.URL),
},
},
})
12 changes: 9 additions & 3 deletions pkg/registry/ingress.go
Original file line number Diff line number Diff line change
@@ -17,6 +17,8 @@
package registry

import (
"fmt"

registryv1alpha1 "github.com/devfile/registry-operator/api/v1alpha1"
networkingv1 "k8s.io/api/networking/v1"
"k8s.io/apimachinery/pkg/runtime"
@@ -26,7 +28,7 @@ import (
func GenerateIngress(cr *registryv1alpha1.DevfileRegistry, host string, scheme *runtime.Scheme, labels map[string]string) *networkingv1.Ingress {
pathTypeImplementationSpecific := networkingv1.PathTypeImplementationSpecific
ingress := &networkingv1.Ingress{
ObjectMeta: generateObjectMeta(IngressName(cr.Name), cr.Namespace, labels),
ObjectMeta: generateObjectMeta(IngressName(cr), cr.Namespace, labels),
Spec: networkingv1.IngressSpec{
Rules: []networkingv1.IngressRule{
{
@@ -38,7 +40,7 @@ func GenerateIngress(cr *registryv1alpha1.DevfileRegistry, host string, scheme *
Path: "/",
Backend: networkingv1.IngressBackend{
Service: &networkingv1.IngressServiceBackend{
Name: ServiceName(cr.Name),
Name: ServiceName(cr),
Port: networkingv1.ServiceBackendPort{
Number: int32(DevfileIndexPort),
},
@@ -70,5 +72,9 @@ func GenerateIngress(cr *registryv1alpha1.DevfileRegistry, host string, scheme *
}

func GetDevfileRegistryIngress(cr *registryv1alpha1.DevfileRegistry) string {
return cr.Name + "." + cr.Spec.K8s.IngressDomain
return GetHostname(cr) + "." + cr.Spec.K8s.IngressDomain
}

func GetHostname(cr *registryv1alpha1.DevfileRegistry) string {
return fmt.Sprintf("%s-%s", getAppFullName(cr), cr.Namespace)
}
90 changes: 90 additions & 0 deletions pkg/registry/ingress_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
//
//
// Copyright Red Hat
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package registry

import (
"testing"

registryv1alpha1 "github.com/devfile/registry-operator/api/v1alpha1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func TestGetDevfileRegistryIngress(t *testing.T) {

tests := []struct {
name string
cr registryv1alpha1.DevfileRegistry
want string
}{
{
name: "Case 1: Correct Conjunction",
cr: registryv1alpha1.DevfileRegistry{
ObjectMeta: metav1.ObjectMeta{
Name: "test-name",
Namespace: "test-namespace",
},
Spec: registryv1alpha1.DevfileRegistrySpec{
K8s: registryv1alpha1.DevfileRegistrySpecK8sOnly{
IngressDomain: "my-domain",
},
}},
want: "test-name-devfile-registry-test-namespace.my-domain",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ingress := GetDevfileRegistryIngress(&tt.cr)
if ingress != tt.want {
t.Errorf("expected: %v got: %v", tt.want, ingress)
}
})
}

}

func TestGetHostname(t *testing.T) {

tests := []struct {
name string
cr registryv1alpha1.DevfileRegistry
want string
}{
{
name: "Case 1: Correct Hostname",
cr: registryv1alpha1.DevfileRegistry{
ObjectMeta: metav1.ObjectMeta{
Name: "test-name",
Namespace: "test-namespace",
},
Spec: registryv1alpha1.DevfileRegistrySpec{
K8s: registryv1alpha1.DevfileRegistrySpecK8sOnly{
IngressDomain: "my-domain",
},
}},
want: "test-name-devfile-registry-test-namespace",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
hostname := GetHostname(&tt.cr)
if hostname != tt.want {
t.Errorf("expected: %v got: %v", tt.want, hostname)
}
})
}

}
46 changes: 29 additions & 17 deletions pkg/registry/naming.go
Original file line number Diff line number Diff line change
@@ -16,36 +16,48 @@

package registry

// genericResourceName returns just the name of the custom resource, to be used
func GenericResourceName(devfileRegistryName string) string {
return devfileRegistryName
import (
registryv1alpha1 "github.com/devfile/registry-operator/api/v1alpha1"
)

// GenericResourceName returns just the fully qualified app name, to be used
func GenericResourceName(cr *registryv1alpha1.DevfileRegistry) string {
return getAppFullName(cr)
}

// DeploymentName returns the name of the deployment object associated with the DevfileRegistry CR
// Just returns the CR name right now, but extracting to a function to avoid relying on that assumption in the future
func DeploymentName(devfileRegistryName string) string {
return GenericResourceName(devfileRegistryName)
// Just returns the fully qualified app name right now, but extracting to a function to avoid relying on that assumption in the future
func DeploymentName(cr *registryv1alpha1.DevfileRegistry) string {
return GenericResourceName(cr)
}

// ServiceName returns the name of the service object associated with the DevfileRegistry CR
// Just returns the CR name right now, but extracting to a function to avoid relying on that assumption in the future
func ServiceName(devfileRegistryName string) string {
return GenericResourceName(devfileRegistryName)
// Just returns the fully qualified app name right now, but extracting to a function to avoid relying on that assumption in the future
func ServiceName(cr *registryv1alpha1.DevfileRegistry) string {
return GenericResourceName(cr)
}

// ConfigMapName returns the name of the service object associated with the DevfileRegistry CR
func ConfigMapName(devfileRegistryName string) string {
return devfileRegistryName + "-registry-config"
func ConfigMapName(cr *registryv1alpha1.DevfileRegistry) string {
const suffixLength = 15
appFullName := getAppFullName(cr)
configMapNameLength := (len(appFullName) + suffixLength)

if configMapNameLength > maxTruncLength {
return truncateNameLengthN(appFullName, len(appFullName)-suffixLength) + "-registry-config"
}

return appFullName + "-registry-config"
}

// PVCName returns the name of the PVC object associated with the DevfileRegistry CR
// Just returns the CR name right now, but extracting to a function to avoid relying on that assumption in the future
func PVCName(devfileRegistryName string) string {
return GenericResourceName(devfileRegistryName)
// Just returns the fully qualified app name right now, but extracting to a function to avoid relying on that assumption in the future
func PVCName(cr *registryv1alpha1.DevfileRegistry) string {
return GenericResourceName(cr)
}

// IngressName returns the name of the Ingress object associated with the DevfileRegistry CR
// Just returns the CR name right now, but extracting to a function to avoid relying on that assumption in the future
func IngressName(devfileRegistryName string) string {
return GenericResourceName(devfileRegistryName)
// Just returns the fully qualified app name right now, but extracting to a function to avoid relying on that assumption in the future
func IngressName(cr *registryv1alpha1.DevfileRegistry) string {
return GenericResourceName(cr)
}
643 changes: 643 additions & 0 deletions pkg/registry/naming_test.go

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions pkg/registry/route.go
Original file line number Diff line number Diff line change
@@ -30,11 +30,11 @@ func GenerateRoute(cr *registryv1alpha1.DevfileRegistry, scheme *runtime.Scheme,
weight := int32(100)

route := &routev1.Route{
ObjectMeta: generateObjectMeta(IngressName(cr.Name), cr.Namespace, labels),
ObjectMeta: generateObjectMeta(IngressName(cr), cr.Namespace, labels),
Spec: routev1.RouteSpec{
To: routev1.RouteTargetReference{
Kind: "Service",
Name: ServiceName(cr.Name),
Name: ServiceName(cr),
Weight: &weight,
},
Port: &routev1.RoutePort{
2 changes: 1 addition & 1 deletion pkg/registry/service.go
Original file line number Diff line number Diff line change
@@ -27,7 +27,7 @@ import (
// GenerateDevfileRegistryService returns a devfileregistry Service object
func GenerateService(cr *registryv1alpha1.DevfileRegistry, scheme *runtime.Scheme, labels map[string]string) *corev1.Service {
svc := &corev1.Service{
ObjectMeta: generateObjectMeta(ServiceName(cr.Name), cr.Namespace, labels),
ObjectMeta: generateObjectMeta(ServiceName(cr), cr.Namespace, labels),
Spec: corev1.ServiceSpec{
Ports: []corev1.ServicePort{
{
35 changes: 32 additions & 3 deletions pkg/registry/util.go
Original file line number Diff line number Diff line change
@@ -16,7 +16,32 @@

package registry

import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
import (
"strings"

registryv1alpha1 "github.com/devfile/registry-operator/api/v1alpha1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// truncateName truncates given name to default 63 characters,
// trims "-" from suffix if last character
func truncateName(name string) string {
return truncateNameLengthN(name, maxTruncLength)
}

// truncateNameLengthN truncates given name to given n characters,
// trims "-" from suffix if last character
func truncateNameLengthN(name string, n int) string {
if n < 0 {
n = 0
}

if len(name) > n {
return strings.TrimSuffix(name[:n], "-")
}

return strings.TrimSuffix(name, "-")
}

func generateObjectMeta(name string, namespace string, labels map[string]string) metav1.ObjectMeta {
return metav1.ObjectMeta{
@@ -28,6 +53,10 @@ func generateObjectMeta(name string, namespace string, labels map[string]string)

// LabelsForDevfileRegistry returns the labels for selecting the resources
// belonging to the given devfileregistry CR name.
func LabelsForDevfileRegistry(name string) map[string]string {
return map[string]string{"app": "devfileregistry", "devfileregistry_cr": name}
func LabelsForDevfileRegistry(cr *registryv1alpha1.DevfileRegistry) map[string]string {
if cr != nil {
return map[string]string{"app": getAppName(cr), "devfileregistry_cr": cr.Name}
}

return map[string]string{"app": DefaultAppName}
}
190 changes: 190 additions & 0 deletions pkg/registry/util_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
//
//
// Copyright Red Hat
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package registry

import (
"reflect"
"testing"

registryv1alpha1 "github.com/devfile/registry-operator/api/v1alpha1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func Test_truncateName(t *testing.T) {
tests := []struct {
name string
input string
want string
}{
{
name: "Case 1: Short name",
input: "devfile-registry-test",
want: "devfile-registry-test",
},
{
name: "Case 2: Long name",
input: "devfile-registry-testregistry-devfile-io-k8s-prow-test-environment-afdfs2345j2234j2k42ljl234",
want: "devfile-registry-testregistry-devfile-io-k8s-prow-test-environm",
},
{
name: "Case 3: Short name with leftover suffix",
input: "devfile-registry-test-",
want: "devfile-registry-test",
},
{
name: "Case 4: Long name with leftover suffix",
input: "devfile-registry-testregistry-devfile-io-k8s-prow-environment1-tf433",
want: "devfile-registry-testregistry-devfile-io-k8s-prow-environment1",
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
got := truncateName(test.input)
if got != test.want {
t.Errorf("\nGot: %v\nExpected: %v\n", got, test.want)
}
})
}
}

func Test_truncateNameLengthN(t *testing.T) {
tests := []struct {
name string
inputName string
inputLength int
want string
}{
{
name: "Case 1: Short name",
inputName: "devfile-registry-test",
want: "devfile-registry",
inputLength: 17,
},
{
name: "Case 2: Long name",
inputName: "devfile-registry-testregistry-devfile-io-k8s-prow-test-environment-afdfs2345j2234j2k42ljl234",
want: "devfile-registry-testregistry-devfile-io-k8s-prow",
inputLength: 49,
},
{
name: "Case 3: Short name with leftover suffix",
inputName: "devfile-registry-test-",
want: "devfile-registry-test",
inputLength: 30,
},
{
name: "Case 4: Long name with leftover suffix",
inputName: "devfile-registry-testregistry-devfile-io-k8s-prow-environment1-tf433",
want: "devfile-registry-testregistry",
inputLength: 30,
},
{
name: "Case 5: Negative truncation length",
inputName: "devfile-registry-test",
want: "",
inputLength: -17,
},
{
name: "Case 6: Truncation length zero",
inputName: "devfile-registry-test",
want: "",
inputLength: 0,
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
got := truncateNameLengthN(test.inputName, test.inputLength)
if got != test.want {
t.Errorf("\nGot: %v\nExpected: %v\n", got, test.want)
}
})
}
}

func TestLabelsForDevfileRegistry(t *testing.T) {
tests := []struct {
name string
cr *registryv1alpha1.DevfileRegistry
want map[string]string
}{
{
name: "Case 1: Labels with set CR name",
cr: &registryv1alpha1.DevfileRegistry{
ObjectMeta: metav1.ObjectMeta{
Name: "devfile-registry-test",
},
},
want: map[string]string{
"app": DefaultAppName,
"devfileregistry_cr": "devfile-registry-test",
},
},
{
name: "Case 2: Labels with set CR name and app name override",
cr: &registryv1alpha1.DevfileRegistry{
ObjectMeta: metav1.ObjectMeta{
Name: "devfile-registry-test",
},
Spec: registryv1alpha1.DevfileRegistrySpec{
NameOverride: "dr",
},
},
want: map[string]string{
"app": "dr",
"devfileregistry_cr": "devfile-registry-test",
},
},
{
name: "Case 3: Labels with set app name override",
cr: &registryv1alpha1.DevfileRegistry{
Spec: registryv1alpha1.DevfileRegistrySpec{
NameOverride: "dr",
},
},
want: map[string]string{
"app": "dr",
"devfileregistry_cr": "",
},
},
{
name: "Case 4: Labels with empty CR",
cr: &registryv1alpha1.DevfileRegistry{},
want: map[string]string{
"app": DefaultAppName,
"devfileregistry_cr": "",
},
},
{
name: "Case 5: Labels with nil passed as CR",
cr: nil,
want: map[string]string{
"app": DefaultAppName,
},
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
got := LabelsForDevfileRegistry(test.cr)
if !reflect.DeepEqual(got, test.want) {
t.Errorf("\nGot: %v\nExpected: %v\n", got, test.want)
}
})
}
}
4 changes: 2 additions & 2 deletions pkg/registry/volume.go
Original file line number Diff line number Diff line change
@@ -28,10 +28,10 @@ import (
// GenerateDevfileRegistryPVC returns a PVC for providing storage on the OCI registry container
func GeneratePVC(cr *registryv1alpha1.DevfileRegistry, scheme *runtime.Scheme, labels map[string]string) *corev1.PersistentVolumeClaim {
pvc := &corev1.PersistentVolumeClaim{
ObjectMeta: generateObjectMeta(cr.Name, cr.Namespace, labels),
ObjectMeta: generateObjectMeta(PVCName(cr), cr.Namespace, labels),
Spec: corev1.PersistentVolumeClaimSpec{
AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadWriteOnce},
Resources: corev1.ResourceRequirements{
Resources: corev1.VolumeResourceRequirements{
Requests: corev1.ResourceList{
corev1.ResourceStorage: resource.MustParse(getDevfileRegistryVolumeSize(cr)),
},
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
apiVersion: registry.devfile.io/v1alpha1
kind: DevfileRegistry
metadata:
name: devfileregistry-headless
name: devfile-registry-headless
spec:
devfileIndex:
image: quay.io/devfile/devfile-index:next
2 changes: 1 addition & 1 deletion tests/integration/examples/create/devfileregistry-tls.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
apiVersion: registry.devfile.io/v1alpha1
kind: DevfileRegistry
metadata:
name: devfileregistry-tls
name: devfile-registry-tls
spec:
devfileIndex:
image: quay.io/devfile/devfile-index:next
2 changes: 1 addition & 1 deletion tests/integration/examples/create/devfileregistry.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
apiVersion: registry.devfile.io/v1alpha1
kind: DevfileRegistry
metadata:
name: devfileregistry
name: devfile-registry
spec:
devfileIndex:
image: quay.io/devfile/devfile-index:next
2 changes: 1 addition & 1 deletion tests/integration/examples/update/devfileregistry-new.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
apiVersion: registry.devfile.io/v1alpha1
kind: DevfileRegistry
metadata:
name: devfileregistry-update
name: devfile-registry-update
spec:
devfileIndex:
image: quay.io/devfile/devfile-index:next
2 changes: 1 addition & 1 deletion tests/integration/examples/update/devfileregistry-old.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
apiVersion: registry.devfile.io/v1alpha1
kind: DevfileRegistry
metadata:
name: devfileregistry-update
name: devfile-registry-update
spec:
devfileIndex:
image: quay.io/devfile/devfile-index:next
8 changes: 4 additions & 4 deletions tests/integration/pkg/tests/devfileregistry_tests.go
Original file line number Diff line number Diff line change
@@ -33,7 +33,7 @@ var K8sClient *client.K8sClient

var _ = ginkgo.Describe("[Create Devfile Registry resource]", func() {
ginkgo.It("Should deploy a devfile registry on to the cluster", func() {
crName := "devfileregistry"
crName := "devfile-registry"
label := "devfileregistry_cr=" + crName

// Deploy the devfileregistry resource for this test case and wait for the pod to be running
@@ -88,7 +88,7 @@ var _ = ginkgo.Describe("[Create Devfile Registry resource]", func() {

var _ = ginkgo.Describe("[Create Devfile Registry resource with TLS enabled]", func() {
ginkgo.It("Should deploy a devfile registry on to the cluster with HTTPS", func() {
crName := "devfileregistry-tls"
crName := "devfile-registry-tls"
label := "devfileregistry_cr=" + crName

// Deploy the devfileregistry resource for this test case and wait for the pod to be running
@@ -123,7 +123,7 @@ var _ = ginkgo.Describe("[Create Devfile Registry resource with TLS enabled]", f

var _ = ginkgo.Describe("[Create Devfile Registry resource with headless enabled]", func() {
ginkgo.It("Should deploy a headless devfile registry on to the cluster", func() {
crName := "devfileregistry-headless"
crName := "devfile-registry-headless"
label := "devfileregistry_cr=" + crName

// Deploy the devfileregistry resource for this test case and wait for the pod to be running
@@ -165,7 +165,7 @@ var _ = ginkgo.Describe("[Create Devfile Registry resource with headless enabled

var _ = ginkgo.Describe("[Update Devfile Registry resource]", func() {
ginkgo.It("Should deploy a devfile registry on to the cluster and properly update it", func() {
crName := "devfileregistry-update"
crName := "devfile-registry-update"
label := "devfileregistry_cr=" + crName

// Deploy the devfileregistry resource for this test case and wait for the pod to be running