diff --git a/cloud/deployment/fromfile/fromfile.go b/cloud/deployment/fromfile/fromfile.go index ffe35f039..230b9d67f 100644 --- a/cloud/deployment/fromfile/fromfile.go +++ b/cloud/deployment/fromfile/fromfile.go @@ -24,6 +24,7 @@ var ( errInvalidEmail = errors.New("invalid email") errCannotUpdateExistingDeployment = errors.New("already exists") errNotFound = errors.New("does not exist") + errNotPermitted = errors.New("is not permitted") ) const ( @@ -209,6 +210,11 @@ func getCreateOrUpdateInput(deploymentFromFile *inspect.FormattedDeployment, clu WorkerQueues: listQueues, } case updateAction: + // check if cluster is being changed + if clusterID != existingDeployment.Cluster.ID { + return astro.CreateDeploymentInput{}, astro.UpdateDeploymentInput{}, + fmt.Errorf("changing an existing deployment's cluster %w", errNotPermitted) + } updateInput = astro.UpdateDeploymentInput{ ID: existingDeployment.ID, ClusterID: clusterID, diff --git a/cloud/deployment/fromfile/fromfile_test.go b/cloud/deployment/fromfile/fromfile_test.go index 2a5b4e3a9..6ff6e857c 100644 --- a/cloud/deployment/fromfile/fromfile_test.go +++ b/cloud/deployment/fromfile/fromfile_test.go @@ -1792,6 +1792,22 @@ deployment: ID: "test-deployment-id", Label: "test-deployment-label", Description: "description", + Cluster: astro.Cluster{ + ID: "test-cluster-id", + Name: "test-cluster", + NodePools: []astro.NodePool{ + { + ID: "test-pool-id", + IsDefault: false, + NodeInstanceType: "test-worker-1", + }, + { + ID: "test-pool-id-2", + IsDefault: false, + NodeInstanceType: "test-worker-2", + }, + }, + }, } updatedDeployment := astro.Deployment{ ID: "test-deployment-id", @@ -1952,6 +1968,22 @@ deployment: ID: "test-deployment-id", Label: "test-deployment-label", Description: "description", + Cluster: astro.Cluster{ + ID: "test-cluster-id", + Name: "test-cluster", + NodePools: []astro.NodePool{ + { + ID: "test-pool-id", + IsDefault: false, + NodeInstanceType: "test-worker-1", + }, + { + ID: "test-pool-id-2", + IsDefault: false, + NodeInstanceType: "test-worker-2", + }, + }, + }, } updatedDeployment := astro.Deployment{ ID: "test-deployment-id", @@ -2291,6 +2323,22 @@ deployment: ID: "test-deployment-id", Label: "test-deployment-label", Description: "description", + Cluster: astro.Cluster{ + ID: "test-cluster-id", + Name: "test-cluster", + NodePools: []astro.NodePool{ + { + ID: "test-pool-id", + IsDefault: false, + NodeInstanceType: "test-worker-1", + }, + { + ID: "test-pool-id-2", + IsDefault: false, + NodeInstanceType: "test-worker-2", + }, + }, + }, } orgID = "test-org-id" mockWorkerQueueDefaultOptions = astro.WorkerQueueDefaultOptions{ @@ -2777,6 +2825,32 @@ func TestGetCreateOrUpdateInput(t *testing.T) { assert.Equal(t, expectedUpdateDeploymentInput, actualUpdateInput) mockClient.AssertExpectations(t) }) + t.Run("returns an error if the cluster is being changed", func(t *testing.T) { + deploymentID = "test-deployment-id" + deploymentFromFile = inspect.FormattedDeployment{} + expectedUpdateDeploymentInput = astro.UpdateDeploymentInput{} + deploymentFromFile.Deployment.Configuration.ClusterName = "test-cluster-1" + deploymentFromFile.Deployment.Configuration.Name = "test-deployment-modified" + deploymentFromFile.Deployment.Configuration.Description = "test-description" + deploymentFromFile.Deployment.Configuration.RunTimeVersion = "test-runtime-v" + deploymentFromFile.Deployment.Configuration.SchedulerAU = 4 + deploymentFromFile.Deployment.Configuration.SchedulerCount = 2 + existingDeployment := astro.Deployment{ + ID: deploymentID, + Label: "test-deployment", + Cluster: astro.Cluster{ + ID: "test-cluster-id", + }, + } + + expectedUpdateDeploymentInput = astro.UpdateDeploymentInput{} + mockClient := new(astro_mocks.Client) + _, actualUpdateInput, err = getCreateOrUpdateInput(&deploymentFromFile, "diff-cluster", workspaceID, "update", &existingDeployment, nil, mockClient) + assert.ErrorIs(t, err, errNotPermitted) + assert.ErrorContains(t, err, "changing an existing deployment's cluster is not permitted") + assert.Equal(t, expectedUpdateDeploymentInput, actualUpdateInput) + mockClient.AssertExpectations(t) + }) t.Run("returns correct update deployment input when multiple queues are requested", func(t *testing.T) { deploymentID = "test-deployment-id" deploymentFromFile = inspect.FormattedDeployment{} diff --git a/cmd/cloud/deployment.go b/cmd/cloud/deployment.go index 1c838a0ed..21bddc42a 100644 --- a/cmd/cloud/deployment.go +++ b/cmd/cloud/deployment.go @@ -331,6 +331,9 @@ func deploymentUpdate(cmd *cobra.Command, args []string, out io.Writer) error { return errors.Wrap(err, "failed to find a valid workspace") } + // Silence Usage as we have now validated command input + cmd.SilenceUsage = true + // request is to update from a file if inputFile != "" { requestedFlags := cmd.Flags().NFlag() @@ -344,9 +347,6 @@ func deploymentUpdate(cmd *cobra.Command, args []string, out io.Writer) error { return errors.New("Invalid --dag-deploy value)") } - // Silence Usage as we have now validated command input - cmd.SilenceUsage = true - // Get release name from args, if passed if len(args) > 0 { deploymentID = args[0] diff --git a/cmd/cloud/deployment_test.go b/cmd/cloud/deployment_test.go index 48b03361e..70be7b250 100644 --- a/cmd/cloud/deployment_test.go +++ b/cmd/cloud/deployment_test.go @@ -11,6 +11,7 @@ import ( airflowversions "github.com/astronomer/astro-cli/airflow_versions" astro "github.com/astronomer/astro-cli/astro-client" astro_mocks "github.com/astronomer/astro-cli/astro-client/mocks" + "github.com/astronomer/astro-cli/config" "github.com/astronomer/astro-cli/pkg/fileutil" testUtil "github.com/astronomer/astro-cli/pkg/testing" @@ -315,6 +316,20 @@ func TestDeploymentUpdate(t *testing.T) { _, err = execDeploymentCmd(cmdArgs...) assert.Error(t, err) + t.Run("returns an error when getting workspace fails", func(t *testing.T) { + testUtil.InitTestConfig(testUtil.CloudPlatform) + ctx, err := config.GetCurrentContext() + assert.NoError(t, err) + ctx.Workspace = "" + err = ctx.SetContext() + assert.NoError(t, err) + defer testUtil.InitTestConfig(testUtil.CloudPlatform) + expectedOut := "Usage:\n" + cmdArgs := []string{"update", "-n", "doesnotexist"} + resp, err := execDeploymentCmd(cmdArgs...) + assert.ErrorContains(t, err, "failed to find a valid workspace") + assert.Contains(t, resp, expectedOut) + }) t.Run("updates a deployment from file", func(t *testing.T) { orgID := "test-org-id" filePath := "./test-deployment.yaml" @@ -385,8 +400,9 @@ deployment: }, } updatedDeployment := astro.Deployment{ - ID: "test-deployment-id", - Label: "test-deployment-label", + ID: "test-deployment-id", + Label: "test-deployment-label", + Cluster: astro.Cluster{ID: "test-cluster-id", Name: "test-cluster"}, } mockWorkerQueueDefaultOptions := astro.WorkerQueueDefaultOptions{ MinWorkerCount: astro.WorkerQueueOption{