Skip to content

Commit

Permalink
Flux oci support part 4 (#5032)
Browse files Browse the repository at this point in the history
* incremental

* incremental

* incremental

* incremental

* incremental

* incremental

* Antonio's feedback
  • Loading branch information
gfichtenholt authored Jul 9, 2022
1 parent 338632b commit 82a5469
Show file tree
Hide file tree
Showing 15 changed files with 597 additions and 150 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"golang.org/x/sync/semaphore"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
apiv1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/sets"
Expand Down Expand Up @@ -524,12 +525,72 @@ func TestKindClusterAvailablePackageEndpointsForOCI(t *testing.T) {
}

ghUser := os.Getenv("GITHUB_USER")
if ghUser == "" {
t.Fatalf("Environment variable GITHUB_USER needs to be set")
}
ghToken := os.Getenv("GITHUB_TOKEN")
if ghToken == "" {
t.Fatalf("Environment variable GITHUB_TOKEN needs to be set")
if ghUser == "" || ghToken == "" {
t.Fatalf("Environment variables GITHUB_USER and GITHUB_TOKEN need to be set to run this test")
}

testCases := []struct {
testName string
registryUrl string
secret *apiv1.Secret
}{
/*
{
testName: "Testing [" + github_podinfo_oci_registry_url + "] with basic auth secret",
registryUrl: github_podinfo_oci_registry_url,
// this is a secret for authentication with GitHub (ghcr.io)
// personal access token ghp_... can be seen on https://github.com/settings/tokens
// and has "admin:repo_hook, delete_repo, repo" scopes
// one should be able to login successfully like this:
// docker login ghcr.io -u $GITHUB_USER -p $GITHUB_TOKEN AND/OR
// helm registry login ghcr.io -u $GITHUB_USER -p $GITHUB_TOKEN
secret: newBasicAuthSecret(types.NamespacedName{
Name: "oci-repo-secret-" + randSeq(4),
Namespace: "default"},
ghUser,
ghToken,
),
},
*/
{
testName: "Testing [" + github_podinfo_oci_registry_url + "] with dockerconfigjson secret",
registryUrl: github_podinfo_oci_registry_url,
// this is a secret for authentication with GitHub (ghcr.io)
// personal access token ghp_... can be seen on https://github.com/settings/tokens
// and has "admin:repo_hook, delete_repo, repo" scopes
// one should be able to login successfully like this:
// docker login ghcr.io -u $GITHUB_USER -p $GITHUB_TOKEN AND/OR
// helm registry login ghcr.io -u $GITHUB_USER -p $GITHUB_TOKEN
secret: newDockerConfigJsonSecret(types.NamespacedName{
Name: "oci-repo-secret-" + randSeq(4),
Namespace: "default"},
"ghcr.io", ghUser, ghToken,
),
},
// TODO (gfichtenholt) TLS secret

/*
{
// this gets set up in ./testdata/kind-cluster-setup.sh
// currently fails with AuthenticationFailed: failed to log into registry
// 'oci://registry-app-svc.default.svc.cluster.local:5000/helm-charts':
// Get "https://registry-app-svc.default.svc.cluster.local:5000/v2/":
// http: server gave HTTP response to HTTPS client
// the error comes from flux source-controller
// opened a new issue per souleb's request:
// https://github.com/fluxcd/source-controller/issues/805
testName: "Testing [" + in_cluster_oci_registry_url + "]",
registryUrl: in_cluster_oci_registry_url,
secret: newBasicAuthSecret(types.NamespacedName{
Name: "oci-repo-secret-" + randSeq(4),
Namespace: "default"},
"foo",
"bar",
),
},
*/
}

adminName := types.NamespacedName{
Expand All @@ -541,121 +602,121 @@ func TestKindClusterAvailablePackageEndpointsForOCI(t *testing.T) {
t.Fatal(err)
}

repoName := types.NamespacedName{
Name: "my-podinfo-" + randSeq(4),
Namespace: "default",
}
for _, tc := range testCases {
t.Run(tc.testName, func(t *testing.T) {
repoName := types.NamespacedName{
Name: "my-podinfo-" + randSeq(4),
Namespace: "default",
}

// this is a secret for authentication with GitHub (ghcr.io)
// personal access token ghp_... can be seen on https://github.com/settings/tokens
// and has "admin:repo_hook, delete_repo, repo" scopes
// one should be able to login successfully like this:
// docker login ghcr.io -u $GITHUB_USER -p $GITHUB_TOKEN
secretName := ""
if tc.secret != nil {
secretName = tc.secret.Name

ghSecret := newBasicAuthSecret(types.NamespacedName{
Name: "github-secret-1",
Namespace: repoName.Namespace},
ghUser, ghToken)
if err := kubeCreateSecretAndCleanup(t, tc.secret); err != nil {
t.Fatal(err)
}
}

if err := kubeCreateSecretAndCleanup(t, ghSecret); err != nil {
t.Fatal(err)
}
ctx, cancel := context.WithTimeout(grpcContext, defaultContextTimeout)
defer cancel()
setUserManagedSecretsAndCleanup(t, fluxPluginReposClient, ctx, true)
ctx, cancel := context.WithTimeout(grpcContext, defaultContextTimeout)
defer cancel()
setUserManagedSecretsAndCleanup(t, fluxPluginReposClient, ctx, true)

if err := kubeAddHelmRepositoryAndCleanup(
t, repoName, "oci", "oci://ghcr.io/stefanprodan/charts", ghSecret.Name, 0); err != nil {
t.Fatalf("%v", err)
}
// wait until this repo reaches 'Ready'
if err = kubeWaitUntilHelmRepositoryIsReady(t, repoName); err != nil {
t.Fatal(err)
}
if err := kubeAddHelmRepositoryAndCleanup(
t, repoName, "oci", tc.registryUrl, secretName, 0); err != nil {
t.Fatal(err)
}
// wait until this repo reaches 'Ready'
if err = kubeWaitUntilHelmRepositoryIsReady(t, repoName); err != nil {
t.Fatal(err)
}

grpcContext, cancel = context.WithTimeout(grpcContext, defaultContextTimeout)
defer cancel()
resp, err := fluxPluginClient.GetAvailablePackageSummaries(
grpcContext,
&corev1.GetAvailablePackageSummariesRequest{})
if err != nil {
t.Fatalf("%v", err)
}
grpcContext, cancel = context.WithTimeout(grpcContext, defaultContextTimeout)
defer cancel()

opt1 := cmpopts.IgnoreUnexported(
corev1.GetAvailablePackageSummariesResponse{},
corev1.AvailablePackageSummary{},
corev1.AvailablePackageReference{},
corev1.Context{},
plugins.Plugin{},
corev1.PackageAppVersion{})
opt2 := cmpopts.SortSlices(lessAvailablePackageFunc)
if got, want := resp, expected_oci_stefanprodan_podinfo_available_summaries(repoName.Name); !cmp.Equal(got, want, opt1, opt2) {
t.Errorf("mismatch (-want +got):\n%s", cmp.Diff(want, got, opt1, opt2))
}
resp, err := fluxPluginClient.GetAvailablePackageSummaries(
grpcContext,
&corev1.GetAvailablePackageSummariesRequest{})
if err != nil {
t.Fatalf("%v", err)
}

grpcContext, cancel = context.WithTimeout(grpcContext, defaultContextTimeout)
defer cancel()
resp2, err := fluxPluginClient.GetAvailablePackageVersions(
grpcContext, &corev1.GetAvailablePackageVersionsRequest{
AvailablePackageRef: &corev1.AvailablePackageReference{
Context: &corev1.Context{
Namespace: "default",
},
Identifier: repoName.Name + "/podinfo",
},
})
if err != nil {
t.Fatal(err)
}
opts := cmpopts.IgnoreUnexported(
corev1.GetAvailablePackageVersionsResponse{},
corev1.PackageAppVersion{})
if got, want := resp2, expected_versions_stefanprodan_podinfo; !cmp.Equal(want, got, opts) {
t.Errorf("mismatch (-want +got):\n%s", cmp.Diff(want, got, opts))
}
opt1 := cmpopts.IgnoreUnexported(
corev1.GetAvailablePackageSummariesResponse{},
corev1.AvailablePackageSummary{},
corev1.AvailablePackageReference{},
corev1.Context{},
plugins.Plugin{},
corev1.PackageAppVersion{})
opt2 := cmpopts.SortSlices(lessAvailablePackageFunc)
if got, want := resp, expected_oci_stefanprodan_podinfo_available_summaries(repoName.Name); !cmp.Equal(got, want, opt1, opt2) {
t.Errorf("mismatch (-want +got):\n%s", cmp.Diff(want, got, opt1, opt2))
}

grpcContext, cancel = context.WithTimeout(grpcContext, defaultContextTimeout)
defer cancel()
resp3, err := fluxPluginClient.GetAvailablePackageDetail(
grpcContext,
&corev1.GetAvailablePackageDetailRequest{
AvailablePackageRef: &corev1.AvailablePackageReference{
Context: &corev1.Context{
Namespace: "default",
},
Identifier: repoName.Name + "/podinfo",
},
})
if err != nil {
t.Fatal(err)
}
grpcContext, cancel = context.WithTimeout(grpcContext, defaultContextTimeout)
defer cancel()
resp2, err := fluxPluginClient.GetAvailablePackageVersions(
grpcContext, &corev1.GetAvailablePackageVersionsRequest{
AvailablePackageRef: &corev1.AvailablePackageReference{
Context: &corev1.Context{
Namespace: "default",
},
Identifier: repoName.Name + "/podinfo",
},
})
if err != nil {
t.Fatal(err)
}
opts := cmpopts.IgnoreUnexported(
corev1.GetAvailablePackageVersionsResponse{},
corev1.PackageAppVersion{})
if got, want := resp2, expected_versions_stefanprodan_podinfo; !cmp.Equal(want, got, opts) {
t.Errorf("mismatch (-want +got):\n%s", cmp.Diff(want, got, opts))
}

compareActualVsExpectedAvailablePackageDetail(
t,
resp3.AvailablePackageDetail,
expected_detail_oci_stefanprodan_podinfo(repoName.Name).AvailablePackageDetail)
grpcContext, cancel = context.WithTimeout(grpcContext, defaultContextTimeout)
defer cancel()
resp3, err := fluxPluginClient.GetAvailablePackageDetail(
grpcContext,
&corev1.GetAvailablePackageDetailRequest{
AvailablePackageRef: &corev1.AvailablePackageReference{
Context: &corev1.Context{
Namespace: "default",
},
Identifier: repoName.Name + "/podinfo",
},
})
if err != nil {
t.Fatal(err)
}

// try a few older versions
grpcContext, cancel = context.WithTimeout(grpcContext, defaultContextTimeout)
defer cancel()
resp4, err := fluxPluginClient.GetAvailablePackageDetail(
grpcContext,
&corev1.GetAvailablePackageDetailRequest{
AvailablePackageRef: &corev1.AvailablePackageReference{
Context: &corev1.Context{
Namespace: "default",
},
Identifier: repoName.Name + "/podinfo",
},
PkgVersion: "6.1.5",
compareActualVsExpectedAvailablePackageDetail(
t,
resp3.AvailablePackageDetail,
expected_detail_oci_stefanprodan_podinfo(repoName.Name).AvailablePackageDetail)

// try a few older versions
grpcContext, cancel = context.WithTimeout(grpcContext, defaultContextTimeout)
defer cancel()
resp4, err := fluxPluginClient.GetAvailablePackageDetail(
grpcContext,
&corev1.GetAvailablePackageDetailRequest{
AvailablePackageRef: &corev1.AvailablePackageReference{
Context: &corev1.Context{
Namespace: "default",
},
Identifier: repoName.Name + "/podinfo",
},
PkgVersion: "6.1.5",
})
if err != nil {
t.Fatal(err)
}

compareActualVsExpectedAvailablePackageDetail(
t,
resp4.AvailablePackageDetail,
expected_detail_oci_stefanprodan_podinfo_2(repoName.Name).AvailablePackageDetail)
})
if err != nil {
t.Fatal(err)
}

compareActualVsExpectedAvailablePackageDetail(
t,
resp4.AvailablePackageDetail,
expected_detail_oci_stefanprodan_podinfo_2(repoName.Name).AvailablePackageDetail)
}
11 changes: 11 additions & 0 deletions cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/common/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,17 @@ func NewLocalOpaqueSecret(ownerRepo types.NamespacedName) *apiv1.Secret {
}
}

// "Local" in the sense of no namespace is specified
func NewLocalDockerConfigJsonSecret(ownerRepo types.NamespacedName) *apiv1.Secret {
return &apiv1.Secret{
ObjectMeta: metav1.ObjectMeta{
GenerateName: ownerRepo.Name + "-",
},
Type: apiv1.SecretTypeDockerConfigJson,
Data: map[string][]byte{},
}
}

// ref: https://blog.trailofbits.com/2020/06/09/how-to-check-if-a-mutex-is-locked-in-go/
// I understand this is not really "kosher" in general for production usage,
// but in one specific case (cache populateWith() func) it's okay as a confidence test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ func (l *dockerRegistryApiV2RepositoryLister) IsApplicableFor(ociRegistry *OCIRe
if err != nil {
ping = fmt.Sprintf("%v", err)
}
log.Infof("ORAS Registry [%s] Ping: %s", ociRegistry.url.String(), ping)
log.Infof("ORAS v2 Registry [%s PlainHTTP=%t] PING: %s",
ociRegistry.url.String(), orasRegistry.PlainHTTP, ping)
return err == nil, err
}
}
Expand Down
Loading

0 comments on commit 82a5469

Please sign in to comment.