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

Flux oci support #4831

Merged
merged 2 commits into from
Jun 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -932,10 +932,6 @@ func TestChartWithRelativeURL(t *testing.T) {

func newChart(name, namespace string, spec *sourcev1.HelmChartSpec, status *sourcev1.HelmChartStatus) sourcev1.HelmChart {
helmChart := sourcev1.HelmChart{
TypeMeta: metav1.TypeMeta{
Kind: sourcev1.HelmChartKind,
APIVersion: sourcev1.GroupVersion.String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
Generation: int64(1),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,10 +189,6 @@ func kubeAddHelmRepository(t *testing.T, name, url, namespace, secretName string
interval = time.Duration(10 * time.Minute)
}
repo := sourcev1.HelmRepository{
TypeMeta: metav1.TypeMeta{
Kind: sourcev1.HelmRepositoryKind,
APIVersion: sourcev1.GroupVersion.String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
Expand Down Expand Up @@ -435,112 +431,127 @@ func kubeDeleteRole(t *testing.T, name, namespace string) error {
return typedClient.RbacV1().Roles(namespace).Delete(ctx, name, metav1.DeleteOptions{})
}

func kubeCreateServiceAccountAndSecret(t *testing.T, name, namespace string) (string, error) {
func kubeCreateClusterRoleBinding(t *testing.T, name, namespace, role string) error {
t.Logf("+kubeCreateClusterRoleBinding(%s,%s,%s)", name, namespace, role)
typedClient, err := kubeGetTypedClient()
if err != nil {
return "", err
return err
}

ctx, cancel := context.WithTimeout(context.Background(), defaultContextTimeout)
defer cancel()
_, err = typedClient.CoreV1().ServiceAccounts(namespace).Create(

_, err = typedClient.RbacV1().ClusterRoleBindings().Create(
ctx,
&apiv1.ServiceAccount{
&rbacv1.ClusterRoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
Name: name + "-binding",
},
Subjects: []rbacv1.Subject{
{
Kind: rbacv1.ServiceAccountKind,
Name: name,
Namespace: namespace,
},
},
RoleRef: rbacv1.RoleRef{
Kind: "ClusterRole",
Name: role,
},
},
metav1.CreateOptions{})
if err != nil {
return "", err
}
return err
}

secretName := ""
for i := 0; i < 10; i++ {
ctx, cancel := context.WithTimeout(context.Background(), defaultContextTimeout)
defer cancel()
svcAccount, err := typedClient.CoreV1().ServiceAccounts(namespace).Get(ctx, name, metav1.GetOptions{})
if err != nil {
return "", err
}
if len(svcAccount.Secrets) >= 1 && svcAccount.Secrets[0].Name != "" {
secretName = svcAccount.Secrets[0].Name
break
}
t.Logf("Waiting 1s for service account [%s] secret to be set up... [%d/%d]", name, i+1, 10)
time.Sleep(1 * time.Second)
}
if secretName == "" {
return "", fmt.Errorf("Service account [%s] has no secrets", name)
func kubeCreateServiceAccount(t *testing.T, name, namespace string) error {
t.Logf("+kubeCreateServiceAccount(%s,%s)", name, namespace)
typedClient, err := kubeGetTypedClient()
if err != nil {
return err
}

ctx, cancel = context.WithTimeout(context.Background(), defaultContextTimeout)
ctx, cancel := context.WithTimeout(context.Background(), defaultContextTimeout)
defer cancel()
secret, err := typedClient.CoreV1().Secrets(namespace).Get(

_, err = typedClient.CoreV1().ServiceAccounts(namespace).Create(
ctx,
secretName,
metav1.GetOptions{})
if err != nil {
return "", err
}
token := secret.Data["token"]
if token == nil {
return "", err
} else {
return string(token), nil
}
&apiv1.ServiceAccount{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
},
},
metav1.CreateOptions{})
return err
}

func kubeCreateServiceAccountWithClusterRole(t *testing.T, name, namespace, role string) (string, error) {
t.Logf("+kubeCreateServiceAccountWithClusterRole(%s,%s,%s)", name, namespace, role)
token, err := kubeCreateServiceAccountAndSecret(t, name, namespace)

// https://itnext.io/big-change-in-k8s-1-24-about-serviceaccounts-and-their-secrets-4b909a4af4e0
// and
// https://github.com/vmware-tanzu/kubeapps/pull/4772
// it used to be the case that creating service account would automatically create an
// associated secret service account token
// (per https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/)
// but starting with 1.24 it doesn't. So now I do it manually
err := kubeCreateServiceAccount(t, name, namespace)
if err != nil {
return "", err
}
typedClient, err := kubeGetTypedClient()

err = kubeCreateClusterRoleBinding(t, name, namespace, role)
if err != nil {
return "", err
}
ctx, cancel := context.WithTimeout(context.Background(), defaultContextTimeout)
defer cancel()
_, err = typedClient.RbacV1().ClusterRoleBindings().Create(
ctx,
&rbacv1.ClusterRoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: name + "-binding",
},
Subjects: []rbacv1.Subject{
{
Kind: rbacv1.ServiceAccountKind,
Name: name,
Namespace: namespace,
},
},
RoleRef: rbacv1.RoleRef{
Kind: "ClusterRole",
Name: role,

secretName := name + "-token"
err = kubeCreateSecret(t, &apiv1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: secretName,
Namespace: namespace,
Annotations: map[string]string{
apiv1.ServiceAccountNameKey: name,
},
},
metav1.CreateOptions{})
Type: apiv1.SecretTypeServiceAccountToken,
})
if err != nil {
return "", err
}
return string(token), nil

var token string
for i := 0; i < 10; i++ {
token, err = kubeGetSecretToken(t, namespace, secretName, "token")
if token != "" && err == nil {
break
}
t.Logf("Waiting 1s for service account token in secret [%s] to be set up... [%d/%d]", secretName, i+1, 10)
time.Sleep(1 * time.Second)
}

if token == "" {
return "", fmt.Errorf("Failed to get token from secret: [%s]", secretName)
}
return token, nil
}

func kubeCreateServiceAccountWithRoles(t *testing.T, name, namespace string, namespacesToRoles map[string]string) (string, error) {
t.Logf("+kubeCreateServiceAccountWithRoles(%s,%s,%s)", name, namespace, namespacesToRoles)
token, err := kubeCreateServiceAccountAndSecret(t, name, namespace)
err := kubeCreateServiceAccount(t, name, namespace)
if err != nil {
return "", err
}

typedClient, err := kubeGetTypedClient()
if err != nil {
return "", err
}
ctx, cancel := context.WithTimeout(context.Background(), defaultContextTimeout)
defer cancel()

for ns, role := range namespacesToRoles {
ctx, cancel := context.WithTimeout(context.Background(), defaultContextTimeout)
defer cancel()

_, err = typedClient.RbacV1().RoleBindings(ns).Create(
ctx,
&rbacv1.RoleBinding{
Expand All @@ -565,7 +576,36 @@ func kubeCreateServiceAccountWithRoles(t *testing.T, name, namespace string, nam
return "", err
}
}
return string(token), nil

secretName := name + "-token"
err = kubeCreateSecret(t, &apiv1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: secretName,
Namespace: namespace,
Annotations: map[string]string{
apiv1.ServiceAccountNameKey: name,
},
},
Type: apiv1.SecretTypeServiceAccountToken,
})
if err != nil {
return "", err
}

var token string
for i := 0; i < 10; i++ {
token, err = kubeGetSecretToken(t, namespace, secretName, "token")
if token != "" && err == nil {
break
}
t.Logf("Waiting 1s for service account token in secret [%s] to be set up... [%d/%d]", secretName, i+1, 10)
time.Sleep(1 * time.Second)
}

if token == "" {
return "", fmt.Errorf("Failed to get token from secret: [%s]", secretName)
}
return token, nil
}

// ref: https://kubernetes.io/docs/reference/access-authn-authz/rbac/#user-facing-roles
Expand Down Expand Up @@ -1106,6 +1146,10 @@ func redisReceiveNotificationsLoop(t *testing.T, ch <-chan *redis.Message, sem *
}
}

// TODO (gfichtenholt): download bitnami index.yaml once, push it to the flux2testdata pod and use
// that URL to avoid intermittent
// "Failed: failed to fetch Helm repository index: failed to cache index to temporary file: unexpected EOF"

func initNumberOfChartsInBitnamiCatalog(t *testing.T) error {
t.Logf("+initNumberOfChartsInBitnamiCatalog")

Expand Down
4 changes: 0 additions & 4 deletions cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/release.go
Original file line number Diff line number Diff line change
Expand Up @@ -515,10 +515,6 @@ func (s *Server) deleteRelease(ctx context.Context, packageRef *corev1.Installed
// 3. spec.targetNamespace, where flux will install any artifacts from the release
func (s *Server) newFluxHelmRelease(chart *models.Chart, targetName types.NamespacedName, versionExpr string, reconcile *corev1.ReconciliationOptions, values map[string]interface{}) (*helmv2.HelmRelease, error) {
fluxRelease := &helmv2.HelmRelease{
TypeMeta: metav1.TypeMeta{
Kind: helmv2.HelmReleaseKind,
APIVersion: helmv2.GroupVersion.String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: targetName.Name,
Namespace: targetName.Namespace,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1236,10 +1236,6 @@ func compareActualVsExpectedGetInstalledPackageDetailResponse(t *testing.T, actu

func newRelease(name string, namespace string, spec *helmv2.HelmReleaseSpec, status *helmv2.HelmReleaseStatus) helmv2.HelmRelease {
helmRelease := helmv2.HelmRelease{
TypeMeta: metav1.TypeMeta{
Kind: helmv2.HelmReleaseKind,
APIVersion: helmv2.GroupVersion.String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
Generation: int64(1),
Expand Down
4 changes: 0 additions & 4 deletions cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -999,10 +999,6 @@ func newFluxHelmRepo(
pollInterval = metav1.Duration{Duration: time.Duration(interval) * time.Second}
}
fluxRepo := &sourcev1.HelmRepository{
TypeMeta: metav1.TypeMeta{
Kind: sourcev1.HelmRepositoryKind,
APIVersion: sourcev1.GroupVersion.String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: targetName.Name,
Namespace: targetName.Namespace,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,14 @@ func TestKindClusterAddPackageRepository(t *testing.T) {
}
}

func TestKindClusterAddOciPackageRepository(t *testing.T) {
_, _, err := checkEnv(t)
if err != nil {
t.Fatal(err)
}
// TODO
}

func TestKindClusterGetPackageRepositoryDetail(t *testing.T) {
_, fluxPluginReposClient, err := checkEnv(t)
if err != nil {
Expand Down Expand Up @@ -412,10 +420,15 @@ func TestKindClusterGetPackageRepositoryDetail(t *testing.T) {

var resp *corev1.GetPackageRepositoryDetailResponse
for {
grpcCtx, cancel = context.WithTimeout(grpcCtx, defaultContextTimeout)
defer cancel()

resp, err = fluxPluginReposClient.GetPackageRepositoryDetail(grpcCtx, tc.request)

if got, want := status.Code(err), tc.expectedStatusCode; got != want {
t.Fatalf("got: %v, want: %v", err, want)
t.Fatalf("got: %v, want: %v, last repo detail: %v", err, want, resp)
}

if tc.expectedStatusCode != codes.OK {
// we are done
return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2146,10 +2146,6 @@ func TestDeletePackageRepository(t *testing.T) {

func newRepo(name string, namespace string, spec *sourcev1.HelmRepositorySpec, status *sourcev1.HelmRepositoryStatus) sourcev1.HelmRepository {
helmRepository := sourcev1.HelmRepository{
TypeMeta: metav1.TypeMeta{
Kind: sourcev1.HelmRepositoryKind,
APIVersion: sourcev1.GroupVersion.String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
Generation: 1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -521,7 +521,7 @@ func (s *Server) AddPackageRepository(ctx context.Context, request *corev1.AddPa
name := types.NamespacedName{Name: request.Name, Namespace: request.Context.Namespace}

if request.GetNamespaceScoped() {
return nil, status.Errorf(codes.Unimplemented, "Namespaced-scoped repositories are not supported")
return nil, status.Errorf(codes.Unimplemented, "namespaced-scoped repositories are not supported")
} else if request.GetType() != "helm" {
return nil, status.Errorf(codes.Unimplemented, "repository type [%s] not supported", request.GetType())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ FROM nginx:1.21.3
COPY ./nginx.conf /etc/nginx/nginx.conf

# only has a single user: foo, password: bar
COPY ./.htpasswd /etc/apache2/.htpasswd
COPY ./md5.htpasswd /etc/apache2/.htpasswd

COPY ./charts/podinfo-index.yaml /usr/share/nginx/html/podinfo/index.yaml
COPY ./charts/podinfo-6.0.0.tgz /usr/share/nginx/html/podinfo/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
foo:$2y$05$I3zrLMxbfQryP9pUw7EauuXmNJVgBO5Oa4JqA8OPR7QqZ8HHuD5m6
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,22 @@ function deploy {
kubectl set env deployment/fluxv2plugin-testdata-app DOMAIN=cluster --context kind-kubeapps
kubectl expose deployment fluxv2plugin-testdata-app --port=80 --target-port=80 --name=fluxv2plugin-testdata-svc --context kind-kubeapps
kubectl expose deployment fluxv2plugin-testdata-app --port=443 --target-port=443 --name=fluxv2plugin-testdata-ssl-svc --context kind-kubeapps
# set up OCI registry
# ref https://helm.sh/docs/topics/registries/
# only has a single user: foo, password: bar
docker rm -f registry
docker run -dp 5000:5000 --restart=always --name registry \
-v $(pwd)/bcrypt.htpasswd:/etc/docker/registry/auth.htpasswd \
-e REGISTRY_AUTH="{htpasswd: {realm: localhost, path: /etc/docker/registry/auth.htpasswd}}" \
registry
helm registry login -u foo localhost:5000 -p bar
helm push charts/podinfo-6.0.3.tgz oci://localhost:5000/helm-charts
helm show all oci://localhost:5000/helm-charts/podinfo | head -9
}

function undeploy {
helm registry logout localhost:5000
docker rm -f registry
kubectl delete svc/fluxv2plugin-testdata-svc
kubectl delete svc/fluxv2plugin-testdata-ssl-svc
kubectl delete deployment fluxv2plugin-testdata-app --context kind-kubeapps
Expand Down
2 changes: 1 addition & 1 deletion script/makefiles/deploy-dev.mk
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ deploy-kapp-controller:

# Add the flux controllers used for testing the kubeapps-apis integration.
deploy-flux-controllers:
kubectl --kubeconfig=${CLUSTER_CONFIG} apply -f https://github.com/fluxcd/flux2/releases/download/v0.29.4/install.yaml
kubectl --kubeconfig=${CLUSTER_CONFIG} apply -f https://github.com/fluxcd/flux2/releases/download/v0.30.2/install.yaml

reset-dev:
helm --kubeconfig=${CLUSTER_CONFIG} -n kubeapps delete kubeapps || true
Expand Down