diff --git a/pkg/cmd/adm/register_member.go b/pkg/cmd/adm/register_member.go index ca7589c..97c6f06 100644 --- a/pkg/cmd/adm/register_member.go +++ b/pkg/cmd/adm/register_member.go @@ -4,10 +4,7 @@ import ( "context" "errors" "fmt" - "io" - "net/http" "net/url" - "os" "os/exec" "path/filepath" "strings" @@ -22,7 +19,6 @@ import ( clicontext "github.com/kubesaw/ksctl/pkg/context" "github.com/kubesaw/ksctl/pkg/ioutils" "github.com/kubesaw/ksctl/pkg/utils" - errs "github.com/pkg/errors" authv1 "k8s.io/api/authentication/v1" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -211,6 +207,37 @@ func addCluster(term ioutils.Terminal, SANamespacedName runtimeclient.ObjectKey, term.Printlnf("operation", op) } + // create/update toolchaincluster + term.Printlnf("creating ToolchainCluster representation of %s in %s:", joiningClusterType, clusterJoinToName) + toolchainClusterCR := &toolchainv1alpha1.ToolchainCluster{ObjectMeta: metav1.ObjectMeta{Name: toolchainClusterName, Namespace: clusterJoinToOperatorNamespace}} + op, err = controllerutil.CreateOrUpdate(context.TODO(), clusterJoinToClient, toolchainClusterCR, func() error { + + // update the secret label + labels := kubeConfigSecret.GetLabels() + if labels == nil { + labels = make(map[string]string) + } + labels["namespace"] = joiningOperatorNamespace + if joiningClusterType == "member" { + labels["cluster-role.toolchain.dev.openshift.com/tenant"] = "" + } + + toolchainClusterCR.Spec.APIEndpoint = joiningClusterAPIEndpoint + toolchainClusterCR.Spec.SecretRef.Name = secretName + if insecureSkipTLSVerify { + toolchainClusterCR.Spec.DisabledTLSValidations = []toolchainv1alpha1.TLSValidation{toolchainv1alpha1.TLSAll} + } + + return nil + }) + + if err != nil { + return err + } else { + term.Println("Toolchaincluster successfully reconciled") + term.Printlnf("operation", op) + } + return err } @@ -488,9 +515,9 @@ func (v *registerMemberValidated) perform(ctx *extendedCommandContext, newComman return err } // todo - this will be removed once https://issues.redhat.com/browse/KUBESAW-27 is implemented - if err := runAddClusterScript(ctx, newCommand, configuration.Host, v.args.hostKubeConfig, v.args.hostNamespace, v.args.memberKubeConfig, v.args.memberNamespace, "", v.args.useLetsEncrypt); err != nil { - return err - } + //if err := runAddClusterScript(ctx, newCommand, configuration.Host, v.args.hostKubeConfig, v.args.hostNamespace, v.args.memberKubeConfig, v.args.memberNamespace, "", v.args.useLetsEncrypt); err != nil { + // return err + //} hostToolchainClusterKey := runtimeclient.ObjectKey{ Name: v.hostToolchainClusterName, Namespace: v.args.memberNamespace, @@ -511,10 +538,7 @@ func (v *registerMemberValidated) perform(ctx *extendedCommandContext, newComman ctx.Printlnf("Please check the %s ToolchainCluster ServiceAccount in the %s host cluster.", toolchainClusterSAKey, v.hostApiEndpoint) return err } - // todo - this will be removed once https://issues.redhat.com/browse/KUBESAW-27 is implemented - if err := runAddClusterScript(ctx, newCommand, configuration.Member, v.args.hostKubeConfig, v.args.hostNamespace, v.args.memberKubeConfig, v.args.memberNamespace, v.args.nameSuffix, v.args.useLetsEncrypt); err != nil { - return err - } + memberToolchainClusterKey := runtimeclient.ObjectKey{ Name: v.memberToolchainClusterName, Namespace: v.args.hostNamespace, @@ -563,55 +587,3 @@ func findToolchainClusterForMember(allToolchainClusters []toolchainv1alpha1.Tool } return nil } - -func runAddClusterScript(term ioutils.Terminal, newCommand client.CommandCreator, joiningClusterType configuration.ClusterType, hostKubeconfig, hostNs, memberKubeconfig, memberNs, nameSuffix string, useLetsEncrypt bool) error { - if !term.AskForConfirmation(ioutils.WithMessagef("register the %s cluster by creating a ToolchainCluster CR, a Secret and a new ServiceAccount resource?", joiningClusterType)) { - return nil - } - - script, err := downloadScript(term) - if err != nil { - return err - } - args := []string{script.Name(), "--type", joiningClusterType.String(), "--host-kubeconfig", hostKubeconfig, "--host-ns", hostNs, "--member-kubeconfig", memberKubeconfig, "--member-ns", memberNs} - if len(nameSuffix) > 0 { - args = append(args, "--multi-member", nameSuffix) - } - if useLetsEncrypt { - args = append(args, "--lets-encrypt") - } - term.Printlnf("Command to be called: bash %s\n", strings.Join(args, " ")) - bash := newCommand("bash", args...) - bash.Stdout = os.Stdout - bash.Stderr = os.Stderr - return bash.Run() -} - -func downloadScript(term ioutils.Terminal) (*os.File, error) { - resp, err := http.Get(AddClusterScriptURL) - if err != nil { - return nil, errs.Wrapf(err, "unable to get add-script.sh") - } - if resp.StatusCode < 200 || resp.StatusCode > 299 { - return nil, fmt.Errorf("unable to get add-script.sh - response status %s", resp.Status) - } - defer func() { - if err := resp.Body.Close(); err != nil { - term.Printlnf(err.Error()) - } - }() - // Create the file - file, err := os.CreateTemp("", "add-cluster-*.sh") - if err != nil { - return nil, err - } - defer func() { - if err := file.Close(); err != nil { - term.Printlnf(err.Error()) - } - }() - - // Write the body to file - _, err = io.Copy(file, resp.Body) - return file, err -} diff --git a/pkg/cmd/adm/register_member_test.go b/pkg/cmd/adm/register_member_test.go index f6fb00f..8168350 100644 --- a/pkg/cmd/adm/register_member_test.go +++ b/pkg/cmd/adm/register_member_test.go @@ -11,9 +11,7 @@ import ( "github.com/codeready-toolchain/toolchain-common/pkg/cluster" "github.com/codeready-toolchain/toolchain-common/pkg/test" "github.com/ghodss/yaml" - "github.com/h2non/gock" "github.com/kubesaw/ksctl/pkg/client" - "github.com/kubesaw/ksctl/pkg/configuration" . "github.com/kubesaw/ksctl/pkg/test" "github.com/kubesaw/ksctl/pkg/utils" "github.com/stretchr/testify/assert" @@ -35,11 +33,6 @@ func TestRegisterMember(t *testing.T) { SetFileConfig(t, Host(), Member()) hostKubeconfig := PersistKubeConfigFile(t, HostKubeConfig()) memberKubeconfig := PersistKubeConfigFile(t, MemberKubeConfig()) - gock.New(AddClusterScriptDomain). - Get(AddClusterScriptPath). - Persist(). - Reply(200) - defer gock.OffAll() type CommandCreatorSetup struct { Client runtimeclient.Client @@ -55,6 +48,23 @@ func TestRegisterMember(t *testing.T) { hostDeploymentName := test.NamespacedName("toolchain-host-operator", "host-operator-controller-manager") deployment := newDeployment(hostDeploymentName, 1) deployment.Labels = map[string]string{"olm.owner.namespace": "toolchain-host-operator"} + toolchainClusterMemberSa := corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: "toolchaincluster-member", + Namespace: "toolchain-member-operator", + }, + } + toolchainClusterHostSa := corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: "toolchaincluster-host", + Namespace: "toolchain-host-operator", + }, + } + + test.SetupGockForServiceAccounts(t, "https://cool-server.com", + types.NamespacedName{Name: toolchainClusterMemberSa.Name, Namespace: toolchainClusterMemberSa.Namespace}, + types.NamespacedName{Namespace: toolchainClusterHostSa.Namespace, Name: toolchainClusterHostSa.Name}, + ) // the command creator mocks the execution of the add-cluster.sh. We check that we're passing the correct arguments // and we also create the expected ToolchainCluster objects. @@ -149,7 +159,7 @@ func TestRegisterMember(t *testing.T) { t.Run("produces valid example SPC", func(t *testing.T) { // given term := NewFakeTerminalWithResponse("Y") - newClient, fakeClient := newFakeClientsFromRestConfig(t, deployment) + newClient, fakeClient := newFakeClientsFromRestConfig(t, deployment, &toolchainClusterMemberSa, &toolchainClusterHostSa) ctx := newExtendedCommandContext(term, newClient) addClusterCommand, counter := commandCreator(CommandCreatorSetup{ Client: fakeClient, @@ -609,84 +619,6 @@ func TestRegisterMember(t *testing.T) { }) } -func TestRunAddClusterScriptSuccess(t *testing.T) { - // given - SetFileConfig(t, Host(), Member()) - hostKubeconfig := PersistKubeConfigFile(t, HostKubeConfig()) - memberKubeconfig := PersistKubeConfigFile(t, MemberKubeConfig()) - gock.New(AddClusterScriptDomain). - Get(AddClusterScriptPath). - Persist(). - Reply(200) - defer gock.OffAll() - term := NewFakeTerminalWithResponse("Y") - - test := func(t *testing.T, clusterType configuration.ClusterType, nameSuffix string, letEncrypt bool, additionalExpectedArgs ...string) { - // given - expArgs := []string{"--type", clusterType.String(), "--host-kubeconfig", hostKubeconfig, "--host-ns", "host-ns", "--member-kubeconfig", memberKubeconfig, "--member-ns", "member-ns"} - expArgs = append(expArgs, additionalExpectedArgs...) - ocCommandCreator := NewCommandCreator(t, "echo", "bash", - AssertFirstArgPrefixRestEqual("(.*)/add-cluster-(.*)", expArgs...)) - - // when - err := addCluster(term, ocCommandCreator, clusterType, hostKubeconfig, "host-ns", memberKubeconfig, "member-ns", nameSuffix, letEncrypt) - - // then - require.NoError(t, err) - // on Linux, the output contains `Command to be called: bash /tmp/add-cluster-` - // on macOS, the output contains something like `Command to be called: bash /var/folders/b8/wy8kq7_179l7yswz6gz6qx800000gp/T/add-cluster-369107288.sh` - assert.Contains(t, term.Output(), "Command to be called: bash ") - assert.Contains(t, term.Output(), "add-cluster-") - assert.Contains(t, term.Output(), strings.Join(expArgs, " ")) - } - - for _, clusterType := range configuration.ClusterTypes { - t.Run("for cluster name: "+clusterType.String(), func(t *testing.T) { - t.Run("single toolchain in cluster", func(t *testing.T) { - test(t, clusterType, "", false) - }) - t.Run("single toolchain in cluster with letsencrypt", func(t *testing.T) { - test(t, clusterType, "", true, "--lets-encrypt") - }) - t.Run("multiple toolchains in cluster", func(t *testing.T) { - test(t, clusterType, "asdf", false, "--multi-member", "asdf") - }) - t.Run("multiple toolchains in cluster with letsencrypt", func(t *testing.T) { - test(t, clusterType, "42", true, "--multi-member", "42", "--lets-encrypt") - }) - }) - } -} - -func TestRunAddClusterScriptFailed(t *testing.T) { - // given - SetFileConfig(t, Host(), Member()) - hostKubeconfig := PersistKubeConfigFile(t, HostKubeConfig()) - memberKubeconfig := PersistKubeConfigFile(t, MemberKubeConfig()) - gock.New(AddClusterScriptDomain). - Get(AddClusterScriptPath). - Persist(). - Reply(404) - defer gock.OffAll() - - for _, clusterType := range configuration.ClusterTypes { - t.Run("for cluster name: "+clusterType.String(), func(t *testing.T) { - // given - expArgs := []string{"--type", clusterType.String(), "--host-kubeconfig", hostKubeconfig, "--host-ns", "host-ns", "--member-kubeconfig", memberKubeconfig, "--member-ns", "member-ns", "--lets-encrypt"} - ocCommandCreator := NewCommandCreator(t, "echo", "bash", - AssertFirstArgPrefixRestEqual("(.*)/add-cluster-(.*)", expArgs...)) - term := NewFakeTerminalWithResponse("Y") - - // when - err := addCluster(term, ocCommandCreator, clusterType, hostKubeconfig, "host-ns", memberKubeconfig, "member-ns", "", true) - - // then - require.Error(t, err) - assert.NotContains(t, term.Output(), "Command to be called") - }) - } -} - func whenDeploymentThenUpdated(t *testing.T, fakeClient *test.FakeClient, namespacedName types.NamespacedName, currentReplicas int32, numberOfUpdateCalls *int) func(ctx context.Context, obj runtimeclient.Object, opts ...runtimeclient.UpdateOption) error { return func(ctx context.Context, obj runtimeclient.Object, opts ...runtimeclient.UpdateOption) error { if deployment, ok := obj.(*appsv1.Deployment); ok {