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

[repository] fixes and enhancements for repository update support #5763

Merged
merged 9 commits into from
Dec 15, 2022
150 changes: 136 additions & 14 deletions cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/global_vars_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package main

import (
"encoding/json"
"github.com/vmware-tanzu/kubeapps/cmd/kubeapps-apis/plugins/pkg/k8sutils"
"os"
"time"

Expand Down Expand Up @@ -1123,6 +1124,23 @@ var (
},
}

add_repo_8 = sourcev1.HelmRepository{
TypeMeta: metav1.TypeMeta{
Kind: sourcev1.HelmRepositoryKind,
APIVersion: sourcev1.GroupVersion.String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: "bar",
Namespace: "foo",
ResourceVersion: "1",
Annotations: map[string]string{k8sutils.AnnotationDescriptionKey: "repo desc"},
},
Spec: sourcev1.HelmRepositorySpec{
URL: "http://example.com",
Interval: metav1.Duration{Duration: 10 * time.Minute},
},
}

add_repo_req_1 = &corev1.AddPackageRepositoryRequest{
Name: "bar",
Context: &corev1.Context{Namespace: "foo"},
Expand Down Expand Up @@ -1531,6 +1549,15 @@ var (
},
}

add_repo_req_31 = &corev1.AddPackageRepositoryRequest{
Name: "bar",
Context: &corev1.Context{Namespace: "foo"},
Type: "helm",
NamespaceScoped: true,
Url: "http://example.com",
Description: "repo desc",
}

add_repo_expected_resp = &corev1.AddPackageRepositoryResponse{
PackageRepoRef: repoRef("bar", "foo"),
}
Expand Down Expand Up @@ -3518,6 +3545,36 @@ var (
Auth: secret_1_auth,
}

update_repo_req_22 = &corev1.UpdatePackageRepositoryRequest{
PackageRepoRef: repoRefInReq("repo-1", "namespace-1"),
Url: "http://url.com",
Auth: basic_auth(redactedString, "doe"),
}

update_repo_req_23 = &corev1.UpdatePackageRepositoryRequest{
PackageRepoRef: repoRefInReq("repo-1", "namespace-1"),
Url: "http://url.com",
TlsConfig: tls_config_redacted,
Auth: basic_auth("john", "doe"),
}

update_repo_req_24 = func(ca []byte) *corev1.UpdatePackageRepositoryRequest {
return &corev1.UpdatePackageRepositoryRequest{
PackageRepoRef: repoRefInReq("repo-1", "namespace-1"),
Url: "http://url.com",
TlsConfig: tls_config(ca),
Auth: &corev1.PackageRepositoryAuth{
Type: corev1.PackageRepositoryAuth_PACKAGE_REPOSITORY_AUTH_TYPE_UNSPECIFIED,
},
}
}

update_repo_req_25 = &corev1.UpdatePackageRepositoryRequest{
PackageRepoRef: repoRefInReq("repo-1", "namespace-1"),
Url: "http://url.com",
Description: "test desc",
}

update_repo_resp_1 = &corev1.UpdatePackageRepositoryResponse{
PackageRepoRef: repoRef("repo-1", "namespace-1"),
}
Expand Down Expand Up @@ -3792,26 +3849,80 @@ var (
},
}

foo_bar_auth = &corev1.PackageRepositoryAuth{
Type: corev1.PackageRepositoryAuth_PACKAGE_REPOSITORY_AUTH_TYPE_BASIC_AUTH,
PackageRepoAuthOneOf: &corev1.PackageRepositoryAuth_UsernamePassword{
UsernamePassword: &corev1.UsernamePassword{
Username: "foo",
Password: "bar",
},
update_repo_detail_18 = &corev1.GetPackageRepositoryDetailResponse{
Detail: &corev1.PackageRepositoryDetail{
PackageRepoRef: get_repo_detail_package_resp_ref,
Name: "repo-1",
Description: "",
NamespaceScoped: true,
Type: "helm",
Url: "http://url.com",
Interval: "10m",
Auth: foo_bar_auth_redacted,
Status: repo_status_pending,
},
}

foo_bar_auth_redacted = &corev1.PackageRepositoryAuth{
Type: corev1.PackageRepositoryAuth_PACKAGE_REPOSITORY_AUTH_TYPE_BASIC_AUTH,
PackageRepoAuthOneOf: &corev1.PackageRepositoryAuth_UsernamePassword{
UsernamePassword: &corev1.UsernamePassword{
Username: redactedString,
Password: redactedString,
},
update_repo_detail_19 = &corev1.GetPackageRepositoryDetailResponse{
Detail: &corev1.PackageRepositoryDetail{
PackageRepoRef: get_repo_detail_package_resp_ref,
Name: "repo-1",
Description: "",
NamespaceScoped: true,
Type: "helm",
Url: "http://url.com",
Interval: "10m",
TlsConfig: tls_config_redacted,
Auth: foo_bar_auth_redacted,
Status: repo_status_pending,
},
}

update_repo_detail_20 = &corev1.GetPackageRepositoryDetailResponse{
Detail: &corev1.PackageRepositoryDetail{
PackageRepoRef: get_repo_detail_package_resp_ref,
Name: "repo-1",
Description: "",
NamespaceScoped: true,
Type: "helm",
Url: "http://url.com",
Interval: "10m",
TlsConfig: tls_config_redacted,
Auth: &corev1.PackageRepositoryAuth{PassCredentials: false},
Status: repo_status_pending,
},
}

update_repo_detail_21 = &corev1.GetPackageRepositoryDetailResponse{
Detail: &corev1.PackageRepositoryDetail{
PackageRepoRef: get_repo_detail_package_resp_ref,
Name: "repo-1",
Description: "test desc",
NamespaceScoped: true,
Type: "helm",
Url: "http://url.com",
Interval: "10m",
Auth: &corev1.PackageRepositoryAuth{PassCredentials: false},
Status: repo_status_pending,
},
}

basic_auth = func(username, password string) *corev1.PackageRepositoryAuth {
return &corev1.PackageRepositoryAuth{
Type: corev1.PackageRepositoryAuth_PACKAGE_REPOSITORY_AUTH_TYPE_BASIC_AUTH,
PackageRepoAuthOneOf: &corev1.PackageRepositoryAuth_UsernamePassword{
UsernamePassword: &corev1.UsernamePassword{
Username: username,
Password: password,
},
},
}
}

foo_bar_auth = basic_auth("foo", "bar")

foo_bar_auth_redacted = basic_auth(redactedString, redactedString)

github_auth = func(ghUser, ghToken string) *corev1.PackageRepositoryAuth {
return &corev1.PackageRepositoryAuth{
Type: corev1.PackageRepositoryAuth_PACKAGE_REPOSITORY_AUTH_TYPE_BASIC_AUTH,
Expand All @@ -3838,6 +3949,17 @@ var (

tls_auth_redacted = tls_auth([]byte(redactedString), []byte(redactedString))

tls_config = func(ca []byte) *corev1.PackageRepositoryTlsConfig {
return &corev1.PackageRepositoryTlsConfig{
InsecureSkipVerify: false,
PackageRepoTlsConfigOneOf: &corev1.PackageRepositoryTlsConfig_CertAuthority{
CertAuthority: string(ca),
},
}
}

tls_config_redacted = tls_config([]byte(redactedString))

secret_1_auth = &corev1.PackageRepositoryAuth{
Type: corev1.PackageRepositoryAuth_PACKAGE_REPOSITORY_AUTH_TYPE_BASIC_AUTH,
PackageRepoAuthOneOf: &corev1.PackageRepositoryAuth_SecretRef{
Expand Down
72 changes: 51 additions & 21 deletions cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"context"
"encoding/gob"
"fmt"
"github.com/vmware-tanzu/kubeapps/cmd/kubeapps-apis/plugins/pkg/k8sutils"
"regexp"
"strings"
"time"
Expand Down Expand Up @@ -240,6 +241,7 @@ func (s *Server) newRepo(ctx context.Context, request *corev1.AddPackageReposito
return nil, status.Errorf(codes.Unimplemented, "repository type [%s] not supported", typ)
}

description := request.GetDescription()
url := request.GetUrl()
tlsConfig := request.GetTlsConfig()
if url == "" {
Expand All @@ -251,7 +253,8 @@ func (s *Server) newRepo(ctx context.Context, request *corev1.AddPackageReposito

name := types.NamespacedName{Name: request.Name, Namespace: request.Context.Namespace}
auth := request.GetAuth()
// Get or validate secret resource for auth, not yet stored in K8s

// Get or validate secret resource for auth (stored in K8s in this method)
secret, isSecretKubeappsManaged, err := s.handleRepoSecretForCreate(ctx, name, typ, tlsConfig, auth)
if err != nil {
return nil, err
Expand All @@ -269,9 +272,15 @@ func (s *Server) newRepo(ctx context.Context, request *corev1.AddPackageReposito
return nil, status.Errorf(codes.InvalidArgument, "customDetail could not be parsed due to: %v", err)
}
provider = customDetail.Provider

if provider != "" && provider != "generic" {
if auth != nil && auth.Type != corev1.PackageRepositoryAuth_PACKAGE_REPOSITORY_AUTH_TYPE_UNSPECIFIED {
return nil, status.Errorf(codes.InvalidArgument, "Auth provider cannot be configured in combination with another auth method")
}
}
}

if fluxRepo, err := newFluxHelmRepo(name, typ, url, interval, secret, passCredentials, provider); err != nil {
if fluxRepo, err := newFluxHelmRepo(name, description, typ, url, interval, secret, passCredentials, provider); err != nil {
return nil, err
} else if client, err := s.getClient(ctx, name.Namespace); err != nil {
return nil, err
Expand Down Expand Up @@ -336,9 +345,8 @@ func (s *Server) repoDetail(ctx context.Context, repoRef *corev1.PackageReposito
Identifier: repo.Name,
Plugin: GetPluginDetail(),
},
Name: repo.Name,
// TODO (gfichtenholt) Flux HelmRepository CR doesn't have a designated field for description
Description: "",
Name: repo.Name,
Description: k8sutils.GetDescription(&repo.ObjectMeta),
// flux repositories are now considered to be namespaced, to support the most common cases.
// see discussion at https://github.com/vmware-tanzu/kubeapps/issues/5542
NamespaceScoped: true,
Expand Down Expand Up @@ -390,9 +398,8 @@ func (s *Server) repoSummaries(ctx context.Context, ns string) ([]*corev1.Packag
Identifier: repo.Name,
Plugin: GetPluginDetail(),
},
Name: repo.Name,
// TODO (gfichtenholt) Flux HelmRepository CR doesn't have a designated field for description
Description: "",
Name: repo.Name,
Description: k8sutils.GetDescription(&repo.ObjectMeta),
// flux repositories are now considered to be namespaced, to support the most common cases.
// see discussion at https://github.com/vmware-tanzu/kubeapps/issues/5542
NamespaceScoped: true,
Expand All @@ -406,7 +413,7 @@ func (s *Server) repoSummaries(ctx context.Context, ns string) ([]*corev1.Packag
return summaries, nil
}

func (s *Server) updateRepo(ctx context.Context, repoRef *corev1.PackageRepositoryReference, url string, interval string, tlsConfig *corev1.PackageRepositoryTlsConfig, auth *corev1.PackageRepositoryAuth) (*corev1.PackageRepositoryReference, error) {
func (s *Server) updateRepo(ctx context.Context, repoRef *corev1.PackageRepositoryReference, request *corev1.UpdatePackageRepositoryRequest) (*corev1.PackageRepositoryReference, error) {
key := types.NamespacedName{Namespace: repoRef.GetContext().GetNamespace(), Name: repoRef.GetIdentifier()}
repo, err := s.getRepoInCluster(ctx, key)
if err != nil {
Expand All @@ -421,18 +428,16 @@ func (s *Server) updateRepo(ctx context.Context, repoRef *corev1.PackageReposito
return nil, status.Errorf(codes.Internal, "updates to repositories pending reconciliation are not supported")
}

if url == "" {
if request.Url == "" {
return nil, status.Errorf(codes.InvalidArgument, "repository url may not be empty")
}
repo.Spec.URL = url
repo.Spec.URL = request.Url

// flux does not grok repository description yet
// the only field in customDetail is "provider" and I don't see the need to
// have the user update that. Its not like one repository is going to move from
// GCP to AWS.
// description now supported via annotation
k8sutils.SetDescription(&repo.ObjectMeta, request.Description)

if interval != "" {
if duration, err := pkgutils.ToDuration(interval); err != nil {
if request.Interval != "" {
if duration, err := pkgutils.ToDuration(request.Interval); err != nil {
return nil, status.Errorf(codes.InvalidArgument, "interval is invalid: %v", err)
} else {
repo.Spec.Interval = *duration
Expand All @@ -442,14 +447,13 @@ func (s *Server) updateRepo(ctx context.Context, repoRef *corev1.PackageReposito
repo.Spec.Interval = defaultPollInterval
}

if tlsConfig != nil && tlsConfig.InsecureSkipVerify {
if request.TlsConfig != nil && request.TlsConfig.InsecureSkipVerify {
// ref https://github.com/fluxcd/source-controller/issues/807
return nil, status.Errorf(codes.InvalidArgument, "TLS flag insecureSkipVerify is not supported")
}

// validate and get updated (or newly created) secret
secret, isKubeappsManagedSecret, isSecretUpdated, err :=
s.handleRepoSecretForUpdate(ctx, repo, tlsConfig, auth)
secret, isKubeappsManagedSecret, isSecretUpdated, err := s.handleRepoSecretForUpdate(ctx, repo, request.TlsConfig, request.Auth)
if err != nil {
return nil, err
}
Expand All @@ -462,7 +466,29 @@ func (s *Server) updateRepo(ctx context.Context, repoRef *corev1.PackageReposito
}
}

repo.Spec.PassCredentials = auth != nil && auth.PassCredentials
repo.Spec.PassCredentials = request.Auth != nil && request.Auth.PassCredentials

// Get Flux-specific values
if request.CustomDetail != nil {
customDetail := &v1alpha1.FluxPackageRepositoryCustomDetail{}
if err := request.CustomDetail.UnmarshalTo(customDetail); err != nil {
return nil, status.Errorf(codes.InvalidArgument, "customDetail could not be parsed due to: %v", err)
}
provider := customDetail.Provider

// following fixes for issue5746, the provider is allowed to be configured on update if not previously configured
if provider != "" && provider != "generic" {
if request.Auth != nil && request.Auth.Type != corev1.PackageRepositoryAuth_PACKAGE_REPOSITORY_AUTH_TYPE_UNSPECIFIED {
return nil, status.Errorf(codes.InvalidArgument, "Auth provider cannot be configured in combination with another auth method")
}
if repo.Spec.Provider != "" && repo.Spec.Provider != "generic" && repo.Spec.Provider != provider {
return nil, status.Errorf(codes.InvalidArgument, "Auth provider cannot be changed.")
}
repo.Spec.Provider = provider
} else {
repo.Spec.Provider = ""
}
Comment on lines +480 to +490
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm just trying to understand some of the logic here: on lines 487 we explicitly disallow the provider from being changed if it has been set, but on line 491 we allow it to be set to empty. So, unless I'm missing something, users can change the auth provider, they just need to first set it to empty then set it?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The idea was that changing the provider did not really make sense (the original code was completely ignoring the new value) so we are explicitly rejecting such change.
The main reason to allow to unset the provider is that the user may have tried with the provider and it did not work and as such is switching back to using basic auth for example.
But yes, a side effect would be that you could unset the provider and switch to another one in the next update.

}

// get rid of the status field, since now there will be a new reconciliation
// process and the current status no longer applies. metadata and spec I want
Expand Down Expand Up @@ -887,6 +913,7 @@ func checkRepoGeneration(repo sourcev1.HelmRepository) bool {
// ref https://fluxcd.io/docs/components/source/helmrepositories/
func newFluxHelmRepo(
targetName types.NamespacedName,
desc string,
typ string,
url string,
interval string,
Expand Down Expand Up @@ -914,6 +941,9 @@ func newFluxHelmRepo(
if typ == sourcev1.HelmRepositoryTypeOCI {
fluxRepo.Spec.Type = sourcev1.HelmRepositoryTypeOCI
}
if desc != "" {
k8sutils.SetDescription(&fluxRepo.ObjectMeta, desc)
}
if secret != nil {
fluxRepo.Spec.SecretRef = &fluxmeta.LocalObjectReference{
Name: secret.Name,
Expand Down