Conversation
0576287 to
41abf96
Compare
|
Lots of issues with Helm 3.18 due to the upgrade of ORAS from v1 to v2. Login to container registries is broken: We'll postpone to upgrade to Helm 3.18 for Flux 2.7 |
|
Does the error indicate that since the login failed, there is nothing in the creds file (or it wasn't created) causing a EOF. |
|
Reopening just to test some fixes in CI |
3d5b499 to
e81e6cd
Compare
|
That change related to the floats may not be required because there was a change similar to that that was reverted for 3.18.1 |
|
Ideally, you wouldn't need to make these changes with 3.18.1, but I don't yet understand all the problems |
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
Signed-off-by: Matheus Pimenta <matheuscscp@gmail.com>
| reference: LocalReference{Path: "../testdata/charts/helmchart"}, | ||
| wantValues: chartutil.Values{ | ||
| "replicaCount": float64(1), | ||
| "replicaCount": json.Number("1"), |
There was a problem hiding this comment.
I'd be concerned that 3.18.1 will be broken with this change.
Signed-off-by: Matheus Pimenta <matheuscscp@gmail.com>
|
Tests are passing now, but I still prefer not to ship 3.18.0 |
|
I'll keep this branch around though, it's documenting a few things we need to do:
|
|
I'd still like to understand the file issue |
It's actually simple: oras v1 was writing to The first thing that oras v2 does with this empty file is try to parse a JSON from it, which obviously fails with The fix for our CI to pass here is just not creating and passing down this empty file to helm/oras anymore. We were never reusing this file anywhere else in the reconciliation code except for deleting it at the end, so I think in our case just removing the code that creates it, passes it down to helm/oras and deletes it at the end is enough. |
I hope ORAS v2 can take the auth data directly without having to write files on disk. If that's not the case, then we should look into replacing ORAS with |
I ran this test step by step on the debugger to investigate: source-controller/internal/controller/helmchart_controller_test.go Lines 2412 to 2434 in 98b2edc Looks like helm and oras v2 are keeping the username/password entirely in memory with this: So I don't think oras v2 is writing this to disk, @TerryHowe can probably confirm? Maybe we should use |
|
PS the empty config file issue for oras v2 is now fixed in Helm 3 helm/helm#30932 it will be in the next patch release 3.18.2 which we will be releasing very soon. Although I agree flux should no longer need to create a temp creds file at all anymore. |
|
TLDR: We have a workaround in the current version passing an empty tmp file per reconciliation for Helm/ORAS v1 to write creds to. In Helm 3.18 the file is no longer written to, only read from. Since it's empty, we get an error. Helm fixed this in a 3.18.x patch release, but now that we no longer need to generate this empty tmp file we prefer to remove this code. @hiddeco has a strong opinion about implementing this with a custom Authorizer that bypasses any kind of disk persistence logic (which is fine by me) and left these pointers: https://github.com/helm/helm/blob/v3.18.2/pkg/registry/client.go#L198 https://github.com/helm/helm/blob/v3.18.2/pkg/registry/client.go#L147-L160 import (
"context"
"fmt"
"io"
"github.com/hashicorp/go-cleanhttp"
"helm.sh/helm/v3/pkg/registry"
"oras.land/oras-go/v2/registry/remote"
"oras.land/oras-go/v2/registry/remote/auth"
"oras.land/oras-go/v2/registry/remote/credentials"
"github.com/akuity/kargo/pkg/x/version"
)
type EphemeralAuthorizer struct {
authorizer *auth.Client
store credentials.Store
}
func NewEphemeralAuthorizer() (*EphemeralAuthorizer, error) {
store := credentials.NewMemoryStore()
authorizer := &auth.Client{
Client: cleanhttp.DefaultClient(),
Cache: auth.NewCache(),
Credential: credentials.Credential(store),
}
authorizer.SetUserAgent("Kargo/" + version.GetVersion().Version)
return &EphemeralAuthorizer{
authorizer: authorizer,
store: store,
}, nil
}
func (a *EphemeralAuthorizer) Login(ctx context.Context, host, username, password string) error {
reg, err := remote.NewRegistry(host)
if err != nil {
return err
}
reg.Client = a.authorizer
cred, err := a.authorizer.Credential(ctx, host)
if err != nil {
return fmt.Errorf("fetching credentials for %q: %w", host, err)
}
if err = reg.Ping(ctx); err != nil {
return fmt.Errorf("authenticating to %q: %w", host, err)
}
// NB: Deal with "special" behavior around docker.io and its subdomains.
key := credentials.ServerAddressFromRegistry(host)
key = credentials.ServerAddressFromHostname(key)
return a.store.Put(ctx, key, cred)
}
func NewRegistryClient(authorizer auth.Client) (*registry.Client, error) {
opts := []registry.ClientOption{
registry.ClientOptWriter(io.Discard),
registry.ClientOptAuthorizer(authorizer),
// NB: Options like ClientOptCache and ClientOptHTTPClient do not have
// an effect on the registry client when using the authorizer, as they
// are only set when NewClient constructs a new authorizer internally.
}
return registry.NewClient(opts...)
} |
Updates:
Part of: fluxcd/flux2#5363