Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: OCI source working with public repositories #190

Merged
merged 9 commits into from
Nov 15, 2023

Conversation

wilmardo
Copy link
Contributor

@wilmardo wilmardo commented Nov 3, 2023

Closes #182

Rewrites the OCIFetch function to use the go-containerregistry package instead simple a simple GET that was in FetchAndCache.
Since the code is OCI specific I moved it into the oci.go and left the FetchAndCache untouched.

This makes repositories like GHCR.io and Docker Hub work since they require an oauth flow which is now done by the package.

Has the the fix of #186 implemented as well (for the Fetch and ListChartVersions function, that PR is still relevant for the Has). Most of the go-containerregistry logic was taken from other functions already using the package.

This isn't yet ready for production I think, I need some feedback/input on:

  • error messages and general error management
  • is the logic around cache.invalidate still correct? Compared to the FetchAndCache it replaces (link)

General feedback is of course welcome! I am not as fluent in Go as I would like ;)

The FetchAndCache funtion is now only used by helm classic and could be moved into there to instead of the shared utils.

Tests

Test file:

---
source:
  repo:
    kind: OCI
    disableChartsIndex: true
    url: https://registry.hub.docker.com/bitnamicharts
target:
  repo:
    kind: OCI
    # docker run -d -p 8080:5000 --restart=always --name registry registry:2
    url: http://localhost:8080
charts:
  - rabbitmq-cluster-operator

Current 0.20.1 version:

# charts-syncer -c test.yaml sync
I1103 15:04:12.918464       1 sync.go:34] Using config file: "test.yaml"
W1103 15:04:13.512572       1 sync.go:34] There were some problems loading the information of the requested charts: unexpected response — 401 "Unauthorized", {"errors":[{"code":"UNAUTHORIZED","message":"authentication required","detail":[{"Type":"repository","Class":"","Name":"bitnamicharts/rabbitmq-cluster-operator","Action":"pull"}]}]}
 — from https://registry.hub.docker.com/v2/bitnamicharts/rabbitmq-cluster-operator/tags/list
I1103 15:04:13.513631       1 sync.go:49] There are no charts out of sync!

With this PR:

# charts-syncer -c test.yaml sync
I1103 16:05:59.304245   37791 sync.go:34] Using config file: "test.yaml"
I1103 16:06:43.965725   37791 sync.go:45] There are 55 charts out of sync!
I1103 16:06:43.965748   37791 sync.go:55] Syncing "rabbitmq-cluster-operator-3.2.7" chart...
I1103 16:06:44.435869   37791 sync.go:55] Syncing "rabbitmq-cluster-operator-3.1.1" chart...
I1103 16:06:44.664391   37791 sync.go:55] Syncing "rabbitmq-cluster-operator-3.1.5" chart...
I1103 16:06:44.876668   37791 sync.go:55] Syncing "common-2.13.2" chart...
I1103 16:06:45.085246   37791 sync.go:55] Syncing "common-2.10.0" chart...
I1103 16:06:45.288925   37791 sync.go:55] Syncing "rabbitmq-cluster-operator-3.2.1" chart...
I1103 16:06:45.656256   37791 sync.go:55] Syncing "rabbitmq-cluster-operator-3.2.8" chart...
I1103 16:06:46.036875   37791 sync.go:55] Syncing "rabbitmq-cluster-operator-3.2.10" chart...
I1103 16:06:46.421172   37791 sync.go:55] Syncing "rabbitmq-cluster-operator-3.2.3" chart...
I1103 16:06:46.806111   37791 sync.go:55] Syncing "common-2.11.1" chart...
I1103 16:06:46.887843   37791 sync.go:55] Syncing "rabbitmq-cluster-operator-3.1.3" chart...
I1103 16:06:47.173408   37791 sync.go:55] Syncing "rabbitmq-cluster-operator-3.2.0" chart...
I1103 16:06:47.529619   37791 sync.go:55] Syncing "common-2.4.0" chart...
I1103 16:06:47.584617   37791 sync.go:55] Syncing "common-2.2.4" chart...
I1103 16:06:47.626355   37791 sync.go:55] Syncing "rabbitmq-cluster-operator-3.2.9" chart...
I1103 16:06:48.016706   37791 sync.go:55] Syncing "common-2.6.0" chart...
I1103 16:06:48.179834   37791 sync.go:55] Syncing "common-2.8.0" chart...
I1103 16:06:48.268363   37791 sync.go:55] Syncing "rabbitmq-cluster-operator-3.2.2" chart...
I1103 16:06:48.817650   37791 sync.go:55] Syncing "common-2.12.0" chart...
I1103 16:06:48.959972   37791 sync.go:55] Syncing "rabbitmq-cluster-operator-3.1.2" chart...
I1103 16:06:49.166723   37791 sync.go:55] Syncing "rabbitmq-cluster-operator-3.2.4" chart...
I1103 16:06:49.539003   37791 sync.go:55] Syncing "rabbitmq-cluster-operator-3.2.5" chart...
I1103 16:06:49.924295   37791 sync.go:55] Syncing "rabbitmq-cluster-operator-3.1.4" chart...
I1103 16:06:50.282163   37791 sync.go:55] Syncing "rabbitmq-cluster-operator-3.1.6" chart...
I1103 16:06:50.547488   37791 sync.go:55] Syncing "common-2.2.5" chart...
I1103 16:06:50.633203   37791 sync.go:55] Syncing "rabbitmq-cluster-operator-3.2.6" chart...
I1103 16:06:50.852886   37791 sync.go:55] Syncing "common-2.9.0" chart...
I1103 16:06:50.951787   37791 sync.go:55] Syncing "rabbitmq-cluster-operator-3.8.7" chart...
I1103 16:06:51.126404   37791 sync.go:55] Syncing "rabbitmq-cluster-operator-3.8.8" chart...
I1103 16:06:51.510595   37791 sync.go:55] Syncing "rabbitmq-cluster-operator-3.10.0" chart...
I1103 16:06:51.669309   37791 sync.go:55] Syncing "rabbitmq-cluster-operator-3.8.6" chart...
I1103 16:06:51.767081   37791 sync.go:55] Syncing "rabbitmq-cluster-operator-3.8.9" chart...
I1103 16:06:51.939000   37791 sync.go:55] Syncing "rabbitmq-cluster-operator-3.9.0" chart...
I1103 16:06:52.167144   37791 sync.go:55] Syncing "rabbitmq-cluster-operator-3.7.3" chart...
I1103 16:06:52.396091   37791 sync.go:55] Syncing "rabbitmq-cluster-operator-3.7.4" chart...
I1103 16:06:52.776244   37791 sync.go:55] Syncing "rabbitmq-cluster-operator-3.8.0" chart...
I1103 16:06:52.999721   37791 sync.go:55] Syncing "rabbitmq-cluster-operator-3.8.1" chart...
I1103 16:06:53.263030   37791 sync.go:55] Syncing "rabbitmq-cluster-operator-3.4.2" chart...
I1103 16:06:53.476549   37791 sync.go:55] Syncing "rabbitmq-cluster-operator-3.4.1" chart...
I1103 16:06:53.741953   37791 sync.go:55] Syncing "rabbitmq-cluster-operator-3.3.1" chart...
I1103 16:06:54.129934   37791 sync.go:55] Syncing "rabbitmq-cluster-operator-3.6.5" chart...
I1103 16:06:54.415714   37791 sync.go:55] Syncing "rabbitmq-cluster-operator-3.5.0" chart...
I1103 16:06:54.653070   37791 sync.go:55] Syncing "rabbitmq-cluster-operator-3.6.0" chart...
I1103 16:06:54.924667   37791 sync.go:55] Syncing "rabbitmq-cluster-operator-3.6.1" chart...
I1103 16:06:55.083649   37791 sync.go:55] Syncing "rabbitmq-cluster-operator-3.6.4" chart...
I1103 16:06:55.357564   37791 sync.go:55] Syncing "rabbitmq-cluster-operator-3.6.2" chart...
I1103 16:06:55.669050   37791 sync.go:55] Syncing "rabbitmq-cluster-operator-3.6.3" chart...
I1103 16:06:55.868448   37791 sync.go:55] Syncing "rabbitmq-cluster-operator-3.6.7" chart...
I1103 16:06:56.039458   37791 sync.go:55] Syncing "rabbitmq-cluster-operator-3.6.6" chart...
I1103 16:06:56.201536   37791 sync.go:55] Syncing "rabbitmq-cluster-operator-3.8.2" chart...
I1103 16:06:56.585707   37791 sync.go:55] Syncing "rabbitmq-cluster-operator-3.8.3" chart...
I1103 16:06:56.841684   37791 sync.go:55] Syncing "rabbitmq-cluster-operator-3.3.2" chart...
I1103 16:06:57.124429   37791 sync.go:55] Syncing "rabbitmq-cluster-operator-3.7.2" chart...
I1103 16:06:57.406869   37791 sync.go:55] Syncing "rabbitmq-cluster-operator-3.7.1" chart...
I1103 16:06:57.592433   37791 sync.go:55] Syncing "rabbitmq-cluster-operator-3.7.0" chart...

@wilmardo
Copy link
Contributor Author

wilmardo commented Nov 3, 2023

I will look into the tests failing, I forgot to check if the tests were still working haha.

@jotadrilo
Copy link
Contributor

Thank you so much for your contribution!
The team and I really appreciate it! 🥳

Having said that, most of my comments are purely cosmetics.

Comment on lines 116 to 114
parseOpts := []name.Option{}
if r.insecure {
client = utils.InsecureClient
parseOpts = append(parseOpts, name.Insecure)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe you forgot to actually use these options, right? They should be defined before name.ParseReference and be used there.

To prevent this kind of (human) error, we can define an internal method:

func (r *Repo) parseReference(ref string) (*name.Reference, error)

However, I believe you missed it because it is not necessary by the time you are parsing the reference, but when you are actually fetching the artifact. Is that right?

pkg/client/repo/oci/oci.go Outdated Show resolved Hide resolved
pkg/client/repo/oci/oci.go Outdated Show resolved Hide resolved
pkg/client/repo/oci/oci.go Outdated Show resolved Hide resolved
pkg/client/repo/oci/oci.go Outdated Show resolved Hide resolved
pkg/client/repo/oci/oci.go Outdated Show resolved Hide resolved
pkg/client/repo/oci/oci.go Outdated Show resolved Hide resolved
pkg/client/repo/oci/oci.go Outdated Show resolved Hide resolved
@tompizmor
Copy link
Member

Hi @wilmardo!! Would you have some time to update this MR?

Signed-off-by: wilmardo <info@wilmardenouden.nl>
…ng compare

Signed-off-by: wilmardo <info@wilmardenouden.nl>
@wilmardo
Copy link
Contributor Author

wilmardo commented Nov 14, 2023

@tompizmor @jotadrilo I think I have addressed all the comments, updated the tests and rebased to master.

Also 0457468 will superseed #186
At first I copied that approach but it wasn't correct. I corrected my code plus the Has where #186 was for.

Maybe @makkes could test this MR to be sure.

I tested it locally like this:

---
source:
  repo:
    kind: OCI
    disableChartsIndex: true
    url: https://registry.hub.docker.com/bitnamicharts
target:
  repo:
    kind: OCI
    url: https://localhost:8080 # local test target repo
    auth:
      username: username
      password: password
charts:
  - rabbitmq-cluster-operator

Registry was started like this:

# htpasswd -Bbn username password > auth/htpasswd
# openssl genrsa 1024 > certs/domain.key
# openssl req -new -x509 -nodes -sha1 -days 365 -key certs/domain.key -out certs/domain.crt
# docker run -d \
  --restart=always \
  --name registry \
  -v ./auth:/auth \
  -v ./certs:/certs \
  -e REGISTRY_AUTH=htpasswd \
  -e REGISTRY_AUTH_HTPASSWD_REALM="Registry Realm" \
  -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
  -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
  -e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
  -p 8080:5000 \
  registry:2

Without --insecure

W1114 15:12:41.564439   10312 index.go:127] Failed processing rabbitmq-cluster-operator:3.1.4 chart. The index will remain incomplete.
E1114 15:12:43.025858   10312 index.go:153] unable to explore target repo to check "rabbitmq-cluster-operator-3.1.5" chart: failed checking remote: Get "https://localhost:8080/v2/": tls: failed to verify certificate: x509: “Internet Widgits Pty Ltd” certificate is using a broken key size; GET http://localhost:8080/v2/: unexpected status code 400 Bad Request: Client sent an HTTP request to an HTTPS server.

With --insecure is works :)

Also tested the authentication this way which works, and gives a Unauthorized when removed from the config.

@wilmardo wilmardo requested a review from jotadrilo November 14, 2023 14:15
Signed-off-by: wilmardo <info@wilmardenouden.nl>
Signed-off-by: wilmardo <info@wilmardenouden.nl>
Signed-off-by: wilmardo <info@wilmardenouden.nl>
@tompizmor
Copy link
Member

Thank you so much @wilmardo!! I will give it a try as soon as I can!

@makkes
Copy link
Contributor

makkes commented Nov 14, 2023

Thanks @wilmardo. I took the PR for a spin with the following results:

The config from #190 (comment) works fine. Great job! --insecure flag works as expected.

This config works fine:

---
source:
  repo:
    kind: OCI
    disableChartsIndex: true
    url: https://registry.hub.docker.com/bitnamicharts
target:
  repo:
    kind: OCI
    # docker run -d -p 8080:5000 --restart=always --name registry registry:2
    url: http://localhost:5002
charts:
  - rabbitmq-cluster-operator

This config fails:

---
source:
  repo:
    kind: OCI
    disableChartsIndex: true
    url: http://localhost:5002
target:
  repo:
    kind: OCI
    url: http://localhost:5003
charts:
  - rabbitmq-cluster-operator

The error message is:

W1114 15:45:15.136947 3325759 sync.go:33] There were some problems loading the information of the requested charts: failed to parse repo repository can only contain the characters `abcdefghijklmnopqrstuvwxyz0123456789_-./`: localhost:5002rabbitmq-cluster-operator

@tompizmor
Copy link
Member

@makkes per the error it looks like a / is missing between the URL and the chart name. Can you try with this source URL:

    url: http://localhost:5002

@tompizmor
Copy link
Member

@wilmardo just one minor thing and it will be ready!

Signed-off-by: wilmardo <info@wilmardenouden.nl>
@wilmardo
Copy link
Contributor Author

@makkes Thanks for testing! I overlooked the classic missing/double slash when URL path concatenating addressed in
8a171aa

@tompizmor Will address the minor thing and I will retest the insecure just once more. The missing parseOpts is suggestion it might not be needed and it can be just in the transports.

Signed-off-by: wilmardo <info@wilmardenouden.nl>
@wilmardo
Copy link
Contributor Author

As assumed, that isn't needed (at least for our usecase).

ready for review again :)

Signed-off-by: wilmardo <info@wilmardenouden.nl>
Signed-off-by: wilmardo <info@wilmardenouden.nl>
@wilmardo
Copy link
Contributor Author

wilmardo commented Nov 14, 2023

Found another little edge case using plus-signs in the chartname edfc60a (also unified the ParseReference calls)

SemVer tags that include build information can be pushed and used. OCI registries don't support + as a tag character. Helm translates the + to _ when stored as a tag.
https://helm.sh/docs/topics/registries/#oci-feature-deprecation-and-behavior-changes-with-v380

For example this wasn't working properly:

---
source:
  repo:
    kind: HELM
    url: https://shanestarcher.com/helm-charts/
target:
  repo:
    kind: OCI
    url: https://localhost:8080
charts:
  - helm-exporter

@wilmardo wilmardo requested a review from tompizmor November 14, 2023 17:47
u.Path = path.Join("v2", u.Path, name, "manifests", version)
req, err := http.NewRequestWithContext(ctx, "GET", u.String(), nil)
req.Header.Set("Accept", ImageManifestMediaType)
u.Path = path.Join(u.Path, "/", chartName)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't seem to make sense as path.Join uses forward slashes as separators: "Join joins any number of path elements into a single path, separating them with slashes".

Copy link
Contributor Author

@wilmardo wilmardo Nov 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this is to ensure a / is prepended before the path to do an easy concat later u.Host + u.Path

Example to illustrate the problem:

url = "https://localhost:8080"
u.Path = path.Join("", "/", "testchartName")
u.Path == "/testchartName"

Without this:

url = "https://localhost:8080"
u.Path = path.Join("", "testchartName")
u.Path == "testchartName"

And that will break the concat, since the slash is missing. For when the slash is already present when the url is set like this https://localhost:8080/``path.Join will normalize the slashes.

The root of this problem is the u.Host + u.Path. I need to get the host + path without the schema. I personally found this more readable then this:

fmt.Sprintf("%s:%s", path.Join(strings.TrimPrefix(r.url.String(), fmt.Sprintf("%s://", r.url.Scheme)), chartName), version)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks by the way for peer reviewing this PR! Very helpful!

@wilmardo wilmardo requested a review from makkes November 15, 2023 10:00
Copy link
Member

@tompizmor tompizmor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot for this contribution @wilmardo @makkes!! ❤️

Copy link
Contributor

@jotadrilo jotadrilo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@tompizmor tompizmor merged commit 3c17daf into bitnami:master Nov 15, 2023
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

cannot sync OCI bitnami chart
4 participants