From 152753f7a57eb3f9ee251c3846ee41dd9fecec2c Mon Sep 17 00:00:00 2001 From: Filip Strozik Date: Thu, 7 Nov 2024 14:44:53 +0100 Subject: [PATCH] Remove hana commands (#2237) --- internal/cmd/alpha/alpha.go | 2 - internal/cmd/alpha/hana/check.go | 125 ---------- internal/cmd/alpha/hana/check_test.go | 221 ------------------ internal/cmd/alpha/hana/credentials.go | 103 --------- internal/cmd/alpha/hana/delete.go | 112 --------- internal/cmd/alpha/hana/hana.go | 23 -- internal/cmd/alpha/hana/map.go | 303 ------------------------- internal/cmd/alpha/hana/provision.go | 183 --------------- internal/cmd/alpha/hana/types.go | 27 --- 9 files changed, 1099 deletions(-) delete mode 100644 internal/cmd/alpha/hana/check.go delete mode 100644 internal/cmd/alpha/hana/check_test.go delete mode 100644 internal/cmd/alpha/hana/credentials.go delete mode 100644 internal/cmd/alpha/hana/delete.go delete mode 100644 internal/cmd/alpha/hana/hana.go delete mode 100644 internal/cmd/alpha/hana/map.go delete mode 100644 internal/cmd/alpha/hana/provision.go delete mode 100644 internal/cmd/alpha/hana/types.go diff --git a/internal/cmd/alpha/alpha.go b/internal/cmd/alpha/alpha.go index 648a9aace..c2c05d0da 100644 --- a/internal/cmd/alpha/alpha.go +++ b/internal/cmd/alpha/alpha.go @@ -4,7 +4,6 @@ import ( "github.com/kyma-project/cli.v3/internal/clierror" "github.com/kyma-project/cli.v3/internal/cmd/alpha/access" "github.com/kyma-project/cli.v3/internal/cmd/alpha/add" - "github.com/kyma-project/cli.v3/internal/cmd/alpha/hana" "github.com/kyma-project/cli.v3/internal/cmd/alpha/modules" "github.com/kyma-project/cli.v3/internal/cmd/alpha/oidc" "github.com/kyma-project/cli.v3/internal/cmd/alpha/provision" @@ -30,7 +29,6 @@ func NewAlphaCMD() (*cobra.Command, clierror.Error) { return nil, err } - cmd.AddCommand(hana.NewHanaCMD(kymaConfig)) cmd.AddCommand(provision.NewProvisionCMD()) cmd.AddCommand(referenceinstance.NewReferenceInstanceCMD(kymaConfig)) cmd.AddCommand(access.NewAccessCMD(kymaConfig)) diff --git a/internal/cmd/alpha/hana/check.go b/internal/cmd/alpha/hana/check.go deleted file mode 100644 index 1d639a203..000000000 --- a/internal/cmd/alpha/hana/check.go +++ /dev/null @@ -1,125 +0,0 @@ -package hana - -import ( - "fmt" - "io" - "os" - "strings" - "time" - - "github.com/kyma-project/cli.v3/internal/clierror" - "github.com/kyma-project/cli.v3/internal/cmdcommon" - "github.com/kyma-project/cli.v3/internal/kube/btp" - "github.com/spf13/cobra" -) - -type hanaCheckConfig struct { - *cmdcommon.KymaConfig - - name string - namespace string - timeout time.Duration - - stdout io.Writer - checkCommands []func(config *hanaCheckConfig) clierror.Error -} - -func NewHanaCheckCMD(kymaConfig *cmdcommon.KymaConfig) *cobra.Command { - config := hanaCheckConfig{ - KymaConfig: kymaConfig, - stdout: os.Stdout, - checkCommands: checkCommands, - } - - cmd := &cobra.Command{ - Use: "check", - Short: "Check if the Hana instance is provisioned.", - Long: "Use this command to check if the Hana instance is provisioned on the SAP Kyma platform.", - Run: func(_ *cobra.Command, _ []string) { - clierror.Check(runCheck(&config)) - }, - } - - cmd.Flags().StringVar(&config.name, "name", "", "Name of Hana instance.") - cmd.Flags().StringVar(&config.namespace, "namespace", "default", "Namespace for Hana instance.") - - _ = cmd.MarkFlagRequired("name") - - return cmd -} - -var ( - checkCommands = []func(config *hanaCheckConfig) clierror.Error{ - checkHanaInstance, - checkHanaBinding, - checkHanaBindingURL, - } -) - -func runCheck(config *hanaCheckConfig) clierror.Error { - fmt.Fprintf(config.stdout, "Checking Hana (%s/%s).\n", config.namespace, config.name) - - for _, command := range config.checkCommands { - err := command(config) - if err != nil { - fmt.Fprintln(config.stdout, "Hana is not fully ready.") - return clierror.New("failed to get resource data", "Make sure that Hana was provisioned.") - } - } - fmt.Fprintln(config.stdout, "Hana is fully ready.") - return nil -} - -func checkHanaInstance(config *hanaCheckConfig) clierror.Error { - client, clientErr := config.GetKubeClientWithClierr() - if clientErr != nil { - return clientErr - } - - instance, err := client.Btp().GetServiceInstance(config.Ctx, config.namespace, config.name) - if err != nil { - return clierror.New(err.Error()) - } - - return printResourceState(config.stdout, instance.Status, "Hana instance", config.namespace, config.name) -} - -func checkHanaBinding(config *hanaCheckConfig) clierror.Error { - client, clientErr := config.GetKubeClientWithClierr() - if clientErr != nil { - return clientErr - } - - binding, err := client.Btp().GetServiceBinding(config.Ctx, config.namespace, config.name) - if err != nil { - return clierror.New(err.Error()) - } - - return printResourceState(config.stdout, binding.Status, "Hana binding", config.namespace, config.name) -} - -func checkHanaBindingURL(config *hanaCheckConfig) clierror.Error { - client, clientErr := config.GetKubeClientWithClierr() - if clientErr != nil { - return clientErr - } - - urlName := hanaBindingURLName(config.name) - binding, err := client.Btp().GetServiceBinding(config.Ctx, config.namespace, urlName) - if err != nil { - return clierror.New(err.Error()) - } - - return printResourceState(config.stdout, binding.Status, "Hana URL binding", config.namespace, urlName) -} - -func printResourceState(writer io.Writer, status btp.CommonStatus, printedName, namespace, name string) clierror.Error { - ready := status.IsReady() - if !ready { - fmt.Fprintf(writer, "%s is not ready (%s/%s).\n", printedName, namespace, name) - errMsg := fmt.Sprintf("%s is not ready", strings.ToLower(printedName[:1])+printedName[1:]) - return clierror.New(errMsg, "Wait for provisioning of Hana resources.", "Check if Hana resources started without errors.") - } - fmt.Fprintf(writer, "%s is ready (%s/%s).\n", printedName, namespace, name) - return nil -} diff --git a/internal/cmd/alpha/hana/check_test.go b/internal/cmd/alpha/hana/check_test.go deleted file mode 100644 index b67d510ec..000000000 --- a/internal/cmd/alpha/hana/check_test.go +++ /dev/null @@ -1,221 +0,0 @@ -package hana - -import ( - "bytes" - "context" - "io" - "testing" - - "github.com/kyma-project/cli.v3/internal/clierror" - "github.com/kyma-project/cli.v3/internal/cmdcommon" - "github.com/kyma-project/cli.v3/internal/kube/btp" - kube_fake "github.com/kyma-project/cli.v3/internal/kube/fake" - "github.com/stretchr/testify/require" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" - dynamic_fake "k8s.io/client-go/dynamic/fake" -) - -const ( - hanaInstalledMessage = `Checking Hana (test-namespace/test-name). -Hana is fully ready. -` - hanaNotInstalledMessage = `Checking Hana (test-namespace/test-name). -Hana is not fully ready. -` - testName = "test-name" - testNamespace = "test-namespace" -) - -func Test_runCheck(t *testing.T) { - t.Run("hana is installed message", func(t *testing.T) { - checkCommandsIter := 0 - testCheckCommand := func(config *hanaCheckConfig) clierror.Error { - checkCommandsIter++ - return nil - } - buffer := bytes.NewBuffer([]byte{}) - config := &hanaCheckConfig{ - name: "test-name", - namespace: "test-namespace", - stdout: buffer, - checkCommands: []func(config *hanaCheckConfig) clierror.Error{ - testCheckCommand, - testCheckCommand, - testCheckCommand, - testCheckCommand, - }, - } - - err := runCheck(config) - require.Nil(t, err) - - require.Equal(t, buffer.String(), hanaInstalledMessage) - require.Equal(t, 4, checkCommandsIter) - }) - - t.Run("hana is NOT installed message", func(t *testing.T) { - testErrorCheckCommand := func(config *hanaCheckConfig) clierror.Error { - return clierror.New("test error") - } - buffer := bytes.NewBuffer([]byte{}) - config := &hanaCheckConfig{ - name: "test-name", - namespace: "test-namespace", - stdout: buffer, - checkCommands: []func(config *hanaCheckConfig) clierror.Error{ - testErrorCheckCommand, - }, - } - - err := runCheck(config) - require.NotNil(t, err) - - require.Equal(t, buffer.String(), hanaNotInstalledMessage) - }) -} - -func Test_checkHanaInstance(t *testing.T) { - t.Run("ready", func(t *testing.T) { - name := "test-name" - testHana := fixTestHanaServiceInstance(name, nil) - config := fixCheckConfig(testHana) - err := checkHanaInstance(&config) - require.Nil(t, err) - }) - - t.Run("not found", func(t *testing.T) { - testHana := fixTestHanaServiceInstance("other-name", nil) - config := fixCheckConfig(testHana) - err := checkHanaInstance(&config) - require.NotNil(t, err) - errMsg := err.String() - require.Contains(t, errMsg, "serviceinstances.services.cloud.sap.com \"test-name\" not found") - }) - t.Run("not ready", func(t *testing.T) { - name := "test-name" - testHana := fixTestHanaServiceInstance(name, &map[string]interface{}{}) - config := fixCheckConfig(testHana) - err := checkHanaInstance(&config) - require.NotNil(t, err) - errMsg := err.String() - require.Contains(t, errMsg, "Wait for provisioning of Hana resources") - }) -} - -func Test_checkHanaBinding(t *testing.T) { - t.Run("ready", func(t *testing.T) { - name := "test-name" - testHanaBinding := fixTestHanaServiceBinding(name, nil) - config := fixCheckConfig(testHanaBinding) - err := checkHanaBinding(&config) - require.Nil(t, err) - }) - t.Run("not found", func(t *testing.T) { - testHanaBinding := fixTestHanaServiceBinding("other-name", nil) - config := fixCheckConfig(testHanaBinding) - err := checkHanaBinding(&config) - require.NotNil(t, err) - errMsg := err.String() - require.Contains(t, errMsg, "servicebindings.services.cloud.sap.com \"test-name\" not found") - }) - t.Run("not ready", func(t *testing.T) { - name := "test-name" - testHanaBinding := fixTestHanaServiceBinding(name, &map[string]interface{}{}) - config := fixCheckConfig(testHanaBinding) - err := checkHanaBinding(&config) - require.NotNil(t, err) - errMsg := err.String() - require.Contains(t, errMsg, "hana binding is not ready") - require.Contains(t, errMsg, "Wait for provisioning of Hana resources") - require.Contains(t, errMsg, "Check if Hana resources started without errors") - }) -} - -func Test_checkHanaBindingURL(t *testing.T) { - t.Run("ready", func(t *testing.T) { - urlName := testName + "-url" - testHanaBinding := fixTestHanaServiceBinding(urlName, nil) - config := fixCheckConfig(testHanaBinding) - err := checkHanaBindingURL(&config) - require.Nil(t, err) - }) - t.Run("not found", func(t *testing.T) { - testHanaBinding := fixTestHanaServiceBinding("other-name", nil) - config := fixCheckConfig(testHanaBinding) - err := checkHanaBindingURL(&config) - require.NotNil(t, err) - errMsg := err.String() - require.Contains(t, errMsg, "servicebindings.services.cloud.sap.com \"test-name-url\" not found") - }) - t.Run("not ready", func(t *testing.T) { - urlName := testName + "-url" - testHanaBinding := fixTestHanaServiceBinding(urlName, &map[string]interface{}{}) - config := fixCheckConfig(testHanaBinding) - err := checkHanaBindingURL(&config) - require.NotNil(t, err) - errMsg := err.String() - require.Contains(t, errMsg, "hana URL binding is not ready") - require.Contains(t, errMsg, "Wait for provisioning of Hana resources") - require.Contains(t, errMsg, "Check if Hana resources started without errors") - }) -} - -func fixCheckConfig(objects ...runtime.Object) hanaCheckConfig { - scheme := runtime.NewScheme() - scheme.AddKnownTypes(btp.GVRServiceInstance.GroupVersion()) - dynamic := dynamic_fake.NewSimpleDynamicClient(scheme, objects...) - config := hanaCheckConfig{ - stdout: io.Discard, - KymaConfig: &cmdcommon.KymaConfig{ - Ctx: context.Background(), - KubeClientConfig: &cmdcommon.KubeClientConfig{ - KubeClient: &kube_fake.FakeKubeClient{ - TestBtpInterface: btp.NewClient(dynamic), - }, - }, - }, - name: testName, - namespace: testNamespace, - timeout: 0, - } - return config -} - -func fixTestHanaServiceInstance(name string, status *map[string]interface{}) *unstructured.Unstructured { - return fixTestHanaService("ServiceInstance", name, testNamespace, status) -} - -func fixTestHanaServiceBinding(name string, status *map[string]interface{}) *unstructured.Unstructured { - return fixTestHanaService("ServiceBinding", name, testNamespace, status) -} - -func fixTestHanaService(kind, name, namespace string, status *map[string]interface{}) *unstructured.Unstructured { - if status == nil { - status = &map[string]interface{}{ - "conditions": []interface{}{ - map[string]interface{}{ - "type": "Succeeded", - "status": string(metav1.ConditionTrue), - }, - map[string]interface{}{ - "type": "Ready", - "status": string(metav1.ConditionTrue), - }, - }, - "ready": "True", - } - } - return &unstructured.Unstructured{ - Object: map[string]interface{}{ - "apiVersion": "services.cloud.sap.com/v1", - "kind": kind, - "metadata": map[string]interface{}{ - "name": name, - "namespace": namespace, - }, - "status": *status, - }, - } -} diff --git a/internal/cmd/alpha/hana/credentials.go b/internal/cmd/alpha/hana/credentials.go deleted file mode 100644 index 364a6a59e..000000000 --- a/internal/cmd/alpha/hana/credentials.go +++ /dev/null @@ -1,103 +0,0 @@ -package hana - -import ( - "fmt" - - "github.com/kyma-project/cli.v3/internal/clierror" - "github.com/kyma-project/cli.v3/internal/cmdcommon" - "github.com/spf13/cobra" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -type hanaCredentialsConfig struct { - *cmdcommon.KymaConfig - - name string - namespace string - user bool - password bool -} - -type credentials struct { - username string - password string -} - -func NewHanaCredentialsCMD(kymaConfig *cmdcommon.KymaConfig) *cobra.Command { - config := hanaCredentialsConfig{ - KymaConfig: kymaConfig, - } - - cmd := &cobra.Command{ - Use: "credentials", - Short: "Print credentials of the Hana instance.", - Long: "Use this command to print credentials of the Hana instance on the SAP Kyma platform.", - Run: func(_ *cobra.Command, _ []string) { - clierror.Check(runCredentials(&config)) - }, - } - - cmd.Flags().StringVar(&config.name, "name", "", "Name of Hana instance.") - cmd.Flags().StringVar(&config.namespace, "namespace", "default", "Namespace for Hana instance.") - - cmd.Flags().BoolVar(&config.user, "user", false, "Print only user name") - cmd.Flags().BoolVar(&config.password, "password", false, "Print only password") - - _ = cmd.MarkFlagRequired("name") - cmd.MarkFlagsMutuallyExclusive("user", "password") - - return cmd -} - -func runCredentials(config *hanaCredentialsConfig) clierror.Error { - fmt.Printf("Getting Hana credentials (%s/%s).\n", config.namespace, config.name) - - credentials, err := getHanaCredentials(config) - if err != nil { - return err - } - printCredentials(config, credentials) - return nil -} - -func printCredentials(config *hanaCredentialsConfig, credentials credentials) { - if config.user { - fmt.Printf("%s", credentials.username) - } else if config.password { - fmt.Printf("%s", credentials.password) - } else { - fmt.Printf("Credentials: %s / %s\n", credentials.username, credentials.password) - } -} - -func getHanaCredentials(config *hanaCredentialsConfig) (credentials, clierror.Error) { - client, clientErr := config.GetKubeClientWithClierr() - if clientErr != nil { - return credentials{}, clientErr - } - - secret, err := client.Static().CoreV1().Secrets(config.namespace).Get(config.Ctx, config.name, metav1.GetOptions{}) - if err != nil { - return handleGetHanaCredentialsError(err) - } - return credentials{ - username: string(secret.Data["username"]), - password: string(secret.Data["password"]), - }, nil -} - -func handleGetHanaCredentialsError(err error) (credentials, clierror.Error) { - hints := []string{ - "Make sure that Hana is run and ready to use. You can use command 'kyma hana check'.", - } - - if err.Error() == "Unauthorized" { - hints = append(hints, "Make sure that your kubeconfig has access to kubernetes.") - } - - credErr := clierror.Wrap(err, - clierror.New("failed to get Hana credentials", hints...), - ) - - return credentials{}, credErr -} diff --git a/internal/cmd/alpha/hana/delete.go b/internal/cmd/alpha/hana/delete.go deleted file mode 100644 index 7be89cf3f..000000000 --- a/internal/cmd/alpha/hana/delete.go +++ /dev/null @@ -1,112 +0,0 @@ -package hana - -import ( - "fmt" - - "github.com/kyma-project/cli.v3/internal/clierror" - "github.com/kyma-project/cli.v3/internal/cmdcommon" - "github.com/kyma-project/cli.v3/internal/kube/btp" - "github.com/spf13/cobra" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -type hanaDeleteConfig struct { - *cmdcommon.KymaConfig - - name string - namespace string -} - -func NewHanaDeleteCMD(kymaConfig *cmdcommon.KymaConfig) *cobra.Command { - config := hanaDeleteConfig{ - KymaConfig: kymaConfig, - } - - cmd := &cobra.Command{ - Use: "delete", - Short: "Delete a Hana instance on the Kyma.", - Long: "Use this command to delete a Hana instance on the SAP Kyma platform.", - Run: func(_ *cobra.Command, _ []string) { - clierror.Check(runDelete(&config)) - }, - } - - cmd.Flags().StringVar(&config.name, "name", "", "Name of Hana instance.") - cmd.Flags().StringVar(&config.namespace, "namespace", "default", "Namespace for Hana instance.") - - _ = cmd.MarkFlagRequired("name") - - return cmd -} - -var ( - deleteCommands = []func(*hanaDeleteConfig) clierror.Error{ - deleteHanaBinding, - deleteHanaBindingURL, - deleteHanaInstance, - } -) - -func runDelete(config *hanaDeleteConfig) clierror.Error { - fmt.Printf("Deleting Hana (%s/%s).\n", config.namespace, config.name) - - for _, command := range deleteCommands { - err := command(config) - if err != nil { - return err - } - } - fmt.Println("Operation completed.") - return nil -} - -func deleteHanaInstance(config *hanaDeleteConfig) clierror.Error { - client, clientErr := config.GetKubeClientWithClierr() - if clientErr != nil { - return clientErr - } - - err := client.Dynamic().Resource(btp.GVRServiceInstance). - Namespace(config.namespace). - Delete(config.Ctx, config.name, metav1.DeleteOptions{}) - return handleDeleteResponse(err, "Hana instance", config.namespace, config.name) -} - -func deleteHanaBinding(config *hanaDeleteConfig) clierror.Error { - client, clientErr := config.GetKubeClientWithClierr() - if clientErr != nil { - return clientErr - } - - err := client.Dynamic().Resource(btp.GVRServiceBinding). - Namespace(config.namespace). - Delete(config.Ctx, config.name, metav1.DeleteOptions{}) - return handleDeleteResponse(err, "Hana binding", config.namespace, config.name) -} - -func deleteHanaBindingURL(config *hanaDeleteConfig) clierror.Error { - client, clientErr := config.GetKubeClientWithClierr() - if clientErr != nil { - return clientErr - } - - urlName := hanaBindingURLName(config.name) - err := client.Dynamic().Resource(btp.GVRServiceBinding). - Namespace(config.namespace). - Delete(config.Ctx, urlName, metav1.DeleteOptions{}) - return handleDeleteResponse(err, "Hana URL binding", config.namespace, urlName) -} - -func handleDeleteResponse(err error, printedName, namespace, name string) clierror.Error { - if err == nil { - fmt.Printf("Deleted %s (%s/%s).\n", printedName, namespace, name) - return nil - } - if errors.IsNotFound(err) { - fmt.Printf("%s (%s/%s) not found.\n", printedName, namespace, name) - return nil - } - return clierror.Wrap(err, - clierror.New("failed to delete Hana resource.")) -} diff --git a/internal/cmd/alpha/hana/hana.go b/internal/cmd/alpha/hana/hana.go deleted file mode 100644 index 151ed43ff..000000000 --- a/internal/cmd/alpha/hana/hana.go +++ /dev/null @@ -1,23 +0,0 @@ -package hana - -import ( - "github.com/kyma-project/cli.v3/internal/cmdcommon" - "github.com/spf13/cobra" -) - -func NewHanaCMD(kymaConfig *cmdcommon.KymaConfig) *cobra.Command { - cmd := &cobra.Command{ - Use: "hana", - Short: "Manage a Hana instance on the Kyma platform.", - Long: `Use this command to manage a Hana instance on the SAP Kyma platform.`, - DisableFlagsInUseLine: true, - } - - cmd.AddCommand(NewHanaProvisionCMD(kymaConfig)) - cmd.AddCommand(NewHanaCheckCMD(kymaConfig)) - cmd.AddCommand(NewHanaDeleteCMD(kymaConfig)) - cmd.AddCommand(NewHanaCredentialsCMD(kymaConfig)) - cmd.AddCommand(NewMapHanaCMD(kymaConfig)) - - return cmd -} diff --git a/internal/cmd/alpha/hana/map.go b/internal/cmd/alpha/hana/map.go deleted file mode 100644 index 13aa5de46..000000000 --- a/internal/cmd/alpha/hana/map.go +++ /dev/null @@ -1,303 +0,0 @@ -package hana - -import ( - "bytes" - "encoding/json" - "fmt" - "net/http" - "time" - - "github.com/kyma-project/cli.v3/internal/btp/auth" - "github.com/kyma-project/cli.v3/internal/clierror" - "github.com/kyma-project/cli.v3/internal/cmdcommon" - "github.com/kyma-project/cli.v3/internal/kube/btp" - "github.com/spf13/cobra" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/util/wait" -) - -// this command uses the same config as check command -func NewMapHanaCMD(kymaConfig *cmdcommon.KymaConfig) *cobra.Command { - config := hanaCheckConfig{ - KymaConfig: kymaConfig, - } - - cmd := &cobra.Command{ - Use: "map", - Short: "Map the Hana instance to the Kyma cluster.", - Long: "Use this command to map the Hana instance to the Kyma cluster.", - Run: func(_ *cobra.Command, _ []string) { - clierror.Check(runMap(&config)) - }, - } - - cmd.Flags().StringVar(&config.name, "name", "", "Name of Hana instance.") - cmd.Flags().StringVar(&config.namespace, "namespace", "default", "Namespace for Hana instance.") - cmd.Flags().DurationVar(&config.timeout, "timeout", 7*time.Minute, "Timeout for the command") - - _ = cmd.MarkFlagRequired("name") - - return cmd -} - -var ( - mapCommands = []func(config *hanaCheckConfig) clierror.Error{ - createHanaAPIInstanceIfNeeded, - createHanaAPIBindingIfNeeded, - createHanaInstanceMapping, - } -) - -func runMap(config *hanaCheckConfig) clierror.Error { - for _, command := range mapCommands { - err := command(config) - if err != nil { - return err - } - } - - fmt.Println("Hana instance was successfully mapped to the cluster") - fmt.Println("You may want to create a Hana HDI container: see how to do it under https://help.sap.com/docs/hana-cloud/sap-hana-cloud-getting-started-guide/set-up-hdi-container-kyma") - return nil -} - -func createHanaAPIInstanceIfNeeded(config *hanaCheckConfig) clierror.Error { - client, clientErr := config.GetKubeClientWithClierr() - if clientErr != nil { - return clientErr - } - - // check if instance exists, skip API instance creation if it does - instance, err := client.Btp().GetServiceInstance(config.Ctx, config.namespace, hanaBindingAPIName(config.name)) - if err == nil && instance != nil { - fmt.Printf("Hana API instance already exists (%s/%s)\n", config.namespace, hanaBindingAPIName(config.name)) - return nil - } - return createHanaAPIInstance(config) -} - -func createHanaAPIBindingIfNeeded(config *hanaCheckConfig) clierror.Error { - client, clientErr := config.GetKubeClientWithClierr() - if clientErr != nil { - return clientErr - } - - //check if binding exists, skip API binding creation if it does - instance, err := client.Btp().GetServiceBinding(config.Ctx, config.namespace, hanaBindingAPIName(config.name)) - if err == nil && instance != nil { - fmt.Printf("Hana API instance already exists (%s/%s)\n", config.namespace, hanaBindingAPIName(config.name)) - return nil - } - - return createHanaAPIBinding(config) - -} - -func createHanaAPIInstance(config *hanaCheckConfig) clierror.Error { - client, clientErr := config.GetKubeClientWithClierr() - if clientErr != nil { - return clientErr - } - - instance := hanaAPIInstance(config) - - err := client.Btp().CreateServiceInstance(config.Ctx, instance) - return handleProvisionResponse(err, "Hana API instance", config.namespace, hanaBindingAPIName(config.name)) -} - -func createHanaAPIBinding(config *hanaCheckConfig) clierror.Error { - client, clientErr := config.GetKubeClientWithClierr() - if clientErr != nil { - return clientErr - } - - binding := hanaAPIBinding(config) - - err := client.Btp().CreateServiceBinding(config.Ctx, binding) - return handleProvisionResponse(err, "Hana API binding", config.namespace, hanaBindingAPIName(config.name)) -} - -func hanaAPIInstance(config *hanaCheckConfig) *btp.ServiceInstance { - return &btp.ServiceInstance{ - TypeMeta: metav1.TypeMeta{ - APIVersion: btp.ServicesAPIVersionV1, - Kind: btp.KindServiceInstance, - }, - ObjectMeta: metav1.ObjectMeta{ - Name: hanaBindingAPIName(config.name), - Namespace: config.namespace, - Labels: map[string]string{ - "app.kubernetes.io/name": hanaBindingAPIName(config.name), - }, - }, - Spec: btp.ServiceInstanceSpec{ - Parameters: HanaAPIParameters{ - TechnicalUser: true, - }, - ServiceOfferingName: "hana-cloud", - ServicePlanName: "admin-api-access", - }, - } -} - -func hanaAPIBinding(config *hanaCheckConfig) *btp.ServiceBinding { - instanceName := hanaBindingAPIName(config.name) - return &btp.ServiceBinding{ - TypeMeta: metav1.TypeMeta{ - APIVersion: btp.ServicesAPIVersionV1, - Kind: btp.KindServiceBinding, - }, - ObjectMeta: metav1.ObjectMeta{ - Name: instanceName, - Namespace: config.namespace, - Labels: map[string]string{ - "app.kubernetes.io/name": hanaBindingAPIName(config.name), - }, - }, - Spec: btp.ServiceBindingSpec{ - ServiceInstanceName: instanceName, - SecretName: instanceName, - }, - } -} - -func hanaBindingAPIName(name string) string { - return fmt.Sprintf("%s-api", name) -} - -func createHanaInstanceMapping(config *hanaCheckConfig) clierror.Error { - clusterID, err := getClusterID(config) - if err != nil { - return err - } - - hanaID, err := getHanaID(config) - if err != nil { - return err - } - - // read secret - baseurl, uaa, err := readHanaAPISecret(config) - if err != nil { - return err - } - - // authenticate - token, err := auth.GetOAuthToken("client_credentials", uaa.URL, uaa.ClientID, uaa.ClientSecret) - if err != nil { - return err - } - // create mapping - return hanaInstanceMapping(baseurl, clusterID, hanaID, token.AccessToken) -} - -func getClusterID(config *hanaCheckConfig) (string, clierror.Error) { - client, clientErr := config.GetKubeClientWithClierr() - if clientErr != nil { - return "", clientErr - } - - cm, err := client.Static().CoreV1().ConfigMaps("kyma-system").Get(config.Ctx, "sap-btp-operator-config", metav1.GetOptions{}) - if err != nil { - return "", clierror.Wrap(err, clierror.New("failed to get cluster ID")) - } - return cm.Data["CLUSTER_ID"], nil -} - -func getHanaID(config *hanaCheckConfig) (string, clierror.Error) { - client, clientErr := config.GetKubeClientWithClierr() - if clientErr != nil { - return "", clientErr - } - - // wait for until Hana instance is ready, for default setting it should take 5 minutes - fmt.Print("waiting for Hana instance to be ready... ") - instanceReadyCheck := client.Btp().IsInstanceReady(config.Ctx, config.namespace, config.name) - err := wait.PollUntilContextTimeout(config.Ctx, 10*time.Second, config.timeout, true, instanceReadyCheck) - if err != nil { - fmt.Println("Failed") - return "", clierror.Wrap(err, - clierror.New("timeout while waiting for Hana instance to be ready", "make sure the hana-cloud hana entitlement is enabled"), - ) - } - fmt.Println("done") - - instance, err := client.Btp().GetServiceInstance(config.Ctx, config.namespace, config.name) - if err != nil { - return "", clierror.Wrap(err, clierror.New("failed to get Hana instance")) - } - - return instance.Status.InstanceID, nil -} - -func readHanaAPISecret(config *hanaCheckConfig) (string, *auth.UAA, clierror.Error) { - client, clientErr := config.GetKubeClientWithClierr() - if clientErr != nil { - return "", nil, clientErr - } - - fmt.Print("waiting for Hana API instance to be ready... ") - instanceReadyCheck := client.Btp().IsInstanceReady(config.Ctx, config.namespace, hanaBindingAPIName(config.name)) - err := wait.PollUntilContextTimeout(config.Ctx, 5*time.Second, 2*time.Minute, true, instanceReadyCheck) - if err != nil { - fmt.Println("Failed") - return "", nil, clierror.Wrap(err, - clierror.New("timeout while waiting for Hana API instance", "make sure the hana-cloud admin-api-access entitlement is enabled"), - ) - } - fmt.Println("done") - - fmt.Print("waiting for Hana API binding to be ready... ") - bindingReadyCheck := client.Btp().IsBindingReady(config.Ctx, config.namespace, hanaBindingAPIName(config.name)) - err = wait.PollUntilContextTimeout(config.Ctx, 5*time.Second, 2*time.Minute, true, bindingReadyCheck) - if err != nil { - fmt.Println("Failed") - return "", nil, clierror.Wrap(err, clierror.New("timeout while waiting for Hana API binding")) - } - fmt.Println("done") - secret, err := client.Static().CoreV1().Secrets(config.namespace).Get(config.Ctx, hanaBindingAPIName(config.name), metav1.GetOptions{}) - if err != nil { - return "", nil, clierror.Wrap(err, clierror.New("failed to get secret")) - } - baseURL := secret.Data["baseurl"] - uaaData := secret.Data["uaa"] - - uaa := &auth.UAA{} - err = json.Unmarshal(uaaData, uaa) - if err != nil { - return "", nil, clierror.Wrap(err, clierror.New("failed to decode UAA data")) - } - return string(baseURL), uaa, nil -} - -func hanaInstanceMapping(baseURL, clusterID, hanaID, token string) clierror.Error { - client := &http.Client{} - - requestData := HanaMapping{ - Platform: "kubernetes", - PrimaryID: clusterID, - } - - requestString, err := json.Marshal(requestData) - if err != nil { - return clierror.Wrap(err, clierror.New("failed to create mapping request")) - } - - request, err := http.NewRequest("POST", fmt.Sprintf("https://%s/inventory/v2/serviceInstances/%s/instanceMappings", baseURL, hanaID), bytes.NewBuffer(requestString)) - if err != nil { - return clierror.Wrap(err, clierror.New("failed to create mapping request")) - } - request.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token)) - - resp, err := client.Do(request) - if err != nil { - return clierror.Wrap(err, clierror.New("failed to create mapping")) - } - - // server sends status Created when mapping is created, and 200 if it already exists - if resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusOK { - return clierror.Wrap(fmt.Errorf("status code: %d", resp.StatusCode), clierror.New("failed to create mapping")) - } - - return nil -} diff --git a/internal/cmd/alpha/hana/provision.go b/internal/cmd/alpha/hana/provision.go deleted file mode 100644 index 1545c8a80..000000000 --- a/internal/cmd/alpha/hana/provision.go +++ /dev/null @@ -1,183 +0,0 @@ -package hana - -import ( - "fmt" - - "github.com/kyma-project/cli.v3/internal/clierror" - "github.com/kyma-project/cli.v3/internal/cmdcommon" - "github.com/kyma-project/cli.v3/internal/kube/btp" - "github.com/spf13/cobra" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -type hanaProvisionConfig struct { - *cmdcommon.KymaConfig - - name string - namespace string - planName string - memory int - cpu int - whitelistIP []string -} - -func NewHanaProvisionCMD(kymaConfig *cmdcommon.KymaConfig) *cobra.Command { - config := hanaProvisionConfig{ - KymaConfig: kymaConfig, - } - - cmd := &cobra.Command{ - Use: "provision", - Short: "Provisions a Hana instance on the Kyma.", - Long: "Use this command to provision a Hana instance on the SAP Kyma platform.", - Run: func(_ *cobra.Command, _ []string) { - clierror.Check(runProvision(&config)) - }, - } - - cmd.Flags().StringVar(&config.name, "name", "", "Name of Hana instance.") - cmd.Flags().StringVar(&config.namespace, "namespace", "default", "Namespace for Hana instance.") - cmd.Flags().StringVar(&config.planName, "plan", "hana", "Name of the service plan.") - cmd.Flags().IntVar(&config.memory, "memory", 32, "Memory size for Hana.") //TODO: fulfill proper usage - cmd.Flags().IntVar(&config.cpu, "cpu", 2, "Number of CPUs for Hana.") //TODO: fulfill proper usage - cmd.Flags().StringSliceVar(&config.whitelistIP, "whitelist-ip", []string{"0.0.0.0/0"}, "IP whitelist for Hana.") //TODO: fulfill proper usage - - _ = cmd.MarkFlagRequired("name") - - return cmd -} - -var ( - provisionCommands = []func(*hanaProvisionConfig) clierror.Error{ - createHanaInstance, - createHanaBinding, - createHanaBindingURL, - } -) - -func runProvision(config *hanaProvisionConfig) clierror.Error { - fmt.Printf("Provisioning Hana (%s/%s).\n", config.namespace, config.name) - - for _, command := range provisionCommands { - err := command(config) - if err != nil { - return err - } - } - fmt.Println("Operation completed.") - fmt.Println("You may want to map the Hana instance to use it inside the cluster: see the 'kyma hana map' command.") - return nil -} - -func createHanaInstance(config *hanaProvisionConfig) clierror.Error { - instance := hanaInstance(config) - - client, clientErr := config.GetKubeClientWithClierr() - if clientErr != nil { - return clientErr - } - - err := client.Btp().CreateServiceInstance(config.Ctx, instance) - return handleProvisionResponse(err, "Hana instance", config.namespace, config.name) -} - -func createHanaBinding(config *hanaProvisionConfig) clierror.Error { - binding := hanaBinding(config) - - client, clientErr := config.GetKubeClientWithClierr() - if clientErr != nil { - return clientErr - } - - err := client.Btp().CreateServiceBinding(config.Ctx, binding) - return handleProvisionResponse(err, "Hana binding", config.namespace, config.name) -} - -func createHanaBindingURL(config *hanaProvisionConfig) clierror.Error { - bindingURL := hanaBindingURL(config) - - client, clientErr := config.GetKubeClientWithClierr() - if clientErr != nil { - return clientErr - } - - err := client.Btp().CreateServiceBinding(config.Ctx, bindingURL) - return handleProvisionResponse(err, "Hana URL binding", config.namespace, hanaBindingURLName(config.name)) -} - -func handleProvisionResponse(err error, printedName, namespace, name string) clierror.Error { - if err == nil { - fmt.Printf("Created %s (%s/%s).\n", printedName, namespace, name) - return nil - } - return clierror.Wrap(err, clierror.New("failed to provision Hana resource")) -} - -func hanaInstance(config *hanaProvisionConfig) *btp.ServiceInstance { - return &btp.ServiceInstance{ - TypeMeta: metav1.TypeMeta{ - APIVersion: btp.ServicesAPIVersionV1, - Kind: btp.KindServiceInstance, - }, - ObjectMeta: metav1.ObjectMeta{ - Name: config.name, - }, - Spec: btp.ServiceInstanceSpec{ - ServiceOfferingName: "hana-cloud", // fixed - ServicePlanName: config.planName, - ExternalName: config.name, - Parameters: HanaInstanceParameters{ - Data: HanaInstanceParametersData{ - Memory: config.memory, - Vcpu: config.cpu, - WhitelistIPs: config.whitelistIP, - GenerateSystemPassword: true, // TODO: manage it later - Edition: "cloud", // TODO: is it necessary? - }, - }, - }, - } -} - -func hanaBinding(config *hanaProvisionConfig) *btp.ServiceBinding { - return &btp.ServiceBinding{ - TypeMeta: metav1.TypeMeta{ - APIVersion: btp.ServicesAPIVersionV1, - Kind: btp.KindServiceBinding, - }, - ObjectMeta: metav1.ObjectMeta{ - Name: config.name, - }, - Spec: btp.ServiceBindingSpec{ - ServiceInstanceName: config.name, - ExternalName: config.name, - SecretName: config.name, - Parameters: HanaBindingParameters{ - Scope: "administration", // fixed - CredentialsType: "PASSWORD_GENERATED", // fixed - }, - }, - } -} - -func hanaBindingURL(config *hanaProvisionConfig) *btp.ServiceBinding { - urlName := hanaBindingURLName(config.name) - return &btp.ServiceBinding{ - TypeMeta: metav1.TypeMeta{ - APIVersion: btp.ServicesAPIVersionV1, - Kind: btp.KindServiceBinding, - }, - ObjectMeta: metav1.ObjectMeta{ - Name: urlName, - }, - Spec: btp.ServiceBindingSpec{ - ServiceInstanceName: config.name, - ExternalName: urlName, - SecretName: urlName, - }, - } -} - -func hanaBindingURLName(name string) string { - return fmt.Sprintf("%s-url", name) -} diff --git a/internal/cmd/alpha/hana/types.go b/internal/cmd/alpha/hana/types.go deleted file mode 100644 index 733e1803b..000000000 --- a/internal/cmd/alpha/hana/types.go +++ /dev/null @@ -1,27 +0,0 @@ -package hana - -type HanaAPIParameters struct { - TechnicalUser bool `json:"technicalUser"` -} - -type HanaInstanceParameters struct { - Data HanaInstanceParametersData `json:"data"` -} - -type HanaInstanceParametersData struct { - Memory int `json:"memory"` - Vcpu int `json:"vcpu"` - WhitelistIPs []string `json:"whitelistIPs"` - GenerateSystemPassword bool `json:"generateSystemPassword"` - Edition string `json:"edition"` -} - -type HanaBindingParameters struct { - Scope string `json:"scope"` - CredentialsType string `json:"credential-type"` -} - -type HanaMapping struct { - Platform string `json:"platform"` - PrimaryID string `json:"primaryID"` -}