diff --git a/internal/cmd/alpha/alpha.go b/internal/cmd/alpha/alpha.go index 8e74901bb..4281a09f1 100644 --- a/internal/cmd/alpha/alpha.go +++ b/internal/cmd/alpha/alpha.go @@ -2,6 +2,7 @@ package alpha import ( "context" + "github.com/kyma-project/cli.v3/internal/cmd/alpha/registry" "github.com/kyma-project/cli.v3/internal/cmd/alpha/access" "github.com/kyma-project/cli.v3/internal/cmd/alpha/add" @@ -38,6 +39,7 @@ func NewAlphaCMD() *cobra.Command { cmd.AddCommand(modules.NewModulesCMD(config)) cmd.AddCommand(add.NewAddCMD(config)) cmd.AddCommand(remove.NewRemoveCMD(config)) + cmd.AddCommand(registry.NewRegistryCMD(config)) return cmd } diff --git a/internal/cmd/alpha/imageimport/imageimport.go b/internal/cmd/alpha/imageimport/imageimport.go index 24ee3607c..4c09d9ff9 100644 --- a/internal/cmd/alpha/imageimport/imageimport.go +++ b/internal/cmd/alpha/imageimport/imageimport.go @@ -60,7 +60,7 @@ func (pc *provisionConfig) complete(args []string) clierror.Error { func runImageImport(config *provisionConfig) clierror.Error { // TODO: Add "serverless is not installed" error message - registryConfig, err := registry.GetConfig(config.Ctx, config.KubeClient) + registryConfig, err := registry.GetInternalConfig(config.Ctx, config.KubeClient) if err != nil { return clierror.WrapE(err, clierror.New("failed to load in-cluster registry configuration")) } diff --git a/internal/cmd/alpha/registry/config/config.go b/internal/cmd/alpha/registry/config/config.go new file mode 100644 index 000000000..454b32d02 --- /dev/null +++ b/internal/cmd/alpha/registry/config/config.go @@ -0,0 +1,75 @@ +package config + +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/registry" + "github.com/spf13/cobra" + "os" +) + +type cfgConfig struct { + *cmdcommon.KymaConfig + cmdcommon.KubeClientConfig + + externalurl bool + output string +} + +func NewConfigCMD(kymaConfig *cmdcommon.KymaConfig) *cobra.Command { + cfg := cfgConfig{ + KymaConfig: kymaConfig, + KubeClientConfig: cmdcommon.KubeClientConfig{}, + } + + cmd := &cobra.Command{ + Use: "config", + Short: "Saves Kyma registry dockerconfig to a file", + Long: "Use this command to save Kyma registry dockerconfig to a file", + PreRun: func(_ *cobra.Command, _ []string) { + clierror.Check(cfg.KubeClientConfig.Complete()) + }, + Run: func(_ *cobra.Command, _ []string) { + clierror.Check(runConfig(&cfg)) + }, + } + + cfg.KubeClientConfig.AddFlag(cmd) + cmd.Flags().BoolVar(&cfg.externalurl, "externalurl", false, "External URL for the Kyma registry.") + cmd.Flags().StringVar(&cfg.output, "output", "", "Path where the output file should be saved to. NOTE: docker expects the file to be named `config.json`.") + + return cmd +} + +func runConfig(cfg *cfgConfig) clierror.Error { + registryConfig, err := registry.GetExternalConfig(cfg.Ctx, cfg.KubeClient) + if err != nil { + return clierror.WrapE(err, clierror.New("failed to load in-cluster registry configuration")) + } + + if cfg.externalurl && cfg.output == "" { + fmt.Print(registryConfig.SecretData.PushRegAddr) + return nil + } + + if cfg.externalurl && cfg.output != "" { + writeErr := os.WriteFile(cfg.output, []byte(registryConfig.SecretData.PushRegAddr), os.ModePerm) + if writeErr != nil { + return clierror.New("failed to write docker config to file") + } + return nil + } + + if cfg.output == "" { + fmt.Print(registryConfig.SecretData.DockerConfigJSON) + } else { + writeErr := os.WriteFile(cfg.output, []byte(registryConfig.SecretData.DockerConfigJSON), os.ModePerm) + if writeErr != nil { + return clierror.New("failed to write docker config to file") + } + fmt.Print("Docker config saved to ", cfg.output) + } + + return nil +} diff --git a/internal/cmd/alpha/registry/registry.go b/internal/cmd/alpha/registry/registry.go new file mode 100644 index 000000000..acf45047a --- /dev/null +++ b/internal/cmd/alpha/registry/registry.go @@ -0,0 +1,20 @@ +package registry + +import ( + "github.com/kyma-project/cli.v3/internal/cmd/alpha/registry/config" + "github.com/kyma-project/cli.v3/internal/cmdcommon" + "github.com/spf13/cobra" +) + +func NewRegistryCMD(kymaConfig *cmdcommon.KymaConfig) *cobra.Command { + + cmd := &cobra.Command{ + Use: "registry", + Short: "Set of commands for Kyma registry", + Long: `Use this command manage resources related to Kyma registry`, + } + + cmd.AddCommand(config.NewConfigCMD(kymaConfig)) + + return cmd +} diff --git a/internal/registry/config.go b/internal/registry/config.go index cc23a4143..1d88a3ecf 100644 --- a/internal/registry/config.go +++ b/internal/registry/config.go @@ -15,14 +15,33 @@ import ( "k8s.io/client-go/kubernetes" ) -type RegistryConfig struct { +type ExternalRegistryConfig struct { + SecretName string + SecretData *SecretData +} + +type InternalRegistryConfig struct { SecretName string SecretData *SecretData PodMeta *RegistryPodMeta } -func GetConfig(ctx context.Context, client kube.Client) (*RegistryConfig, clierror.Error) { - config, err := getConfig(ctx, client) +func GetExternalConfig(ctx context.Context, client kube.Client) (*ExternalRegistryConfig, clierror.Error) { + config, err := getExternalConfig(ctx, client) + if err != nil { + return nil, clierror.Wrap(err, + clierror.New("failed to get external registry configuration", + "make sure cluster is available and properly configured", + "make sure the Docker Registry is installed and in Ready/Warning state.", + ), + ) + } + + return config, nil +} + +func GetInternalConfig(ctx context.Context, client kube.Client) (*InternalRegistryConfig, clierror.Error) { + config, err := getInternalConfig(ctx, client) if err != nil { return nil, clierror.Wrap(err, clierror.New("failed to load in-cluster registry configuration", @@ -35,12 +54,30 @@ func GetConfig(ctx context.Context, client kube.Client) (*RegistryConfig, clierr return config, nil } -func getConfig(ctx context.Context, client kube.Client) (*RegistryConfig, error) { +func getExternalConfig(ctx context.Context, client kube.Client) (*ExternalRegistryConfig, error) { dockerRegistry, err := getDockerRegistry(ctx, client.Dynamic()) if err != nil { return nil, err } + if dockerRegistry.Status.ExternalAccess.Enabled == "false" { + return nil, errors.New("external access is not enabled") + } + secretConfig, err := getRegistrySecretData(ctx, client.Static(), dockerRegistry.Status.ExternalAccess.SecretName, dockerRegistry.GetNamespace()) + if err != nil { + return nil, err + } + return &ExternalRegistryConfig{ + SecretName: dockerRegistry.Status.ExternalAccess.SecretName, + SecretData: secretConfig, + }, nil +} + +func getInternalConfig(ctx context.Context, client kube.Client) (*InternalRegistryConfig, error) { + dockerRegistry, err := getDockerRegistry(ctx, client.Dynamic()) + if err != nil { + return nil, err + } secretConfig, err := getRegistrySecretData(ctx, client.Static(), dockerRegistry.Status.InternalAccess.SecretName, dockerRegistry.GetNamespace()) if err != nil { return nil, err @@ -51,7 +88,7 @@ func getConfig(ctx context.Context, client kube.Client) (*RegistryConfig, error) return nil, err } - return &RegistryConfig{ + return &InternalRegistryConfig{ SecretName: dockerRegistry.Status.InternalAccess.SecretName, SecretData: secretConfig, PodMeta: podMeta, diff --git a/internal/registry/config_test.go b/internal/registry/config_test.go index d5ee5dff4..b01b9e4f3 100644 --- a/internal/registry/config_test.go +++ b/internal/registry/config_test.go @@ -16,7 +16,7 @@ import ( ) func TestGetConfig(t *testing.T) { - t.Run("Should return the RegistryConfig", func(t *testing.T) { + t.Run("Should return the InternalRegistryConfig", func(t *testing.T) { // given testRegistrySvc := fixTestRegistrySvc() testRegistryPod := fixTestRegistryPod() @@ -29,7 +29,7 @@ func TestGetConfig(t *testing.T) { scheme.AddKnownTypes(DockerRegistryGVR.GroupVersion(), testDockerRegistry) dynamic := dynamic_fake.NewSimpleDynamicClient(scheme, testDockerRegistry) - expectedRegistryConfig := &RegistryConfig{ + expectedRegistryConfig := &InternalRegistryConfig{ SecretName: testRegistrySecret.GetName(), SecretData: &SecretData{ DockerConfigJSON: string(testRegistrySecret.Data[".dockerconfigjson"]), @@ -51,7 +51,7 @@ func TestGetConfig(t *testing.T) { } // when - config, err := GetConfig(context.Background(), kubeClient) + config, err := GetInternalConfig(context.Background(), kubeClient) // then require.Nil(t, err) @@ -60,7 +60,7 @@ func TestGetConfig(t *testing.T) { } func Test_getRegistrySecretConfig(t *testing.T) { - t.Run("Should return the RegistryConfig", func(t *testing.T) { + t.Run("Should return the InternalRegistryConfig", func(t *testing.T) { // given testRegistrySecret := fixTestRegistrySecret() client := k8s_fake.NewSimpleClientset(testRegistrySecret) diff --git a/internal/registry/types.go b/internal/registry/types.go index 8c1cf29fc..afbe7fe08 100644 --- a/internal/registry/types.go +++ b/internal/registry/types.go @@ -23,8 +23,14 @@ type InternalAccess struct { SecretName string `json:"secretName,omitempty"` } +type ExternalAccess struct { + SecretName string `json:"secretName,omitempty"` + Enabled string `json:"enabled,omitempty"` +} + type DockerRegistryStatus struct { State string `json:"state,omitempty"` Served string `json:"served"` InternalAccess InternalAccess `json:"internalAccess"` + ExternalAccess ExternalAccess `json:"externalAccess"` }