Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduction of astro deploy --image-name=image_name --remote --runtime-version=rt_version #1763

Open
wants to merge 20 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
0e1d75e
Added --image-name flag for software, similar to astri
rujhan-arora-astronomer Dec 12, 2024
6fcb6d0
Fixed test
rujhan-arora-astronomer Dec 12, 2024
293253b
Fixed test
rujhan-arora-astronomer Dec 12, 2024
67da2f7
Fixed test
rujhan-arora-astronomer Dec 12, 2024
4d75ce8
Fixed test
rujhan-arora-astronomer Dec 12, 2024
e4b32ed
1. Split buildPushDockerImage into buildDockerImage(buildDockerImageF…
rujhan-arora-astronomer Dec 13, 2024
1bbd0e3
1. Split buildPushDockerImage into buildDockerImage(buildDockerImageF…
rujhan-arora-astronomer Dec 13, 2024
5979956
Merge branch 'main' into 6828
rujhan-arora-astronomer Dec 16, 2024
cce4de0
Refactored code and added setup and teardown functionalities for --im…
rujhan-arora-astronomer Dec 16, 2024
2764940
Replaced SetupTEst with SetupSuite
rujhan-arora-astronomer Dec 16, 2024
c20a0c3
Replaced SetupTEst with SetupSuite
rujhan-arora-astronomer Dec 16, 2024
c661269
Merge branch 'main' into 6828
rujhan-arora-astronomer Dec 17, 2024
dc21a6f
Moved houstonMock and mockImageHandler to teardown test and subtest f…
rujhan-arora-astronomer Dec 17, 2024
df28c58
Merge branch 'main' into 6828
rujhan-arora-astronomer Dec 17, 2024
fba9fd2
Introduction of astro deploy --image-name=image_name --remote --runti…
rujhan-arora-astronomer Dec 13, 2024
d1a9b6a
Introduction of astro deploy --image-name=image_name --remote --runti…
rujhan-arora-astronomer Dec 13, 2024
cbbf289
Added setup and teardown functions and moved the code for newly added…
rujhan-arora-astronomer Dec 16, 2024
dc52834
Fixed tests
rujhan-arora-astronomer Dec 16, 2024
37939cc
Removed the abstracted out updateDeploymentImageAPICall function
rujhan-arora-astronomer Dec 17, 2024
21dec01
Removed already added TearDownSubTest
rujhan-arora-astronomer Dec 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 30 additions & 11 deletions cmd/software/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,18 @@ var (

ignoreCacheDeploy = false

EnsureProjectDir = utils.EnsureProjectDir
DeployAirflowImage = deploy.Airflow
DagsOnlyDeploy = deploy.DagsOnlyDeploy
isDagOnlyDeploy bool
description string
isImageOnlyDeploy bool
ErrBothDagsOnlyAndImageOnlySet = errors.New("cannot use both --dags and --image together. Run 'astro deploy' to update both your image and dags")
EnsureProjectDir = utils.EnsureProjectDir
DeployAirflowImage = deploy.Airflow
DagsOnlyDeploy = deploy.DagsOnlyDeploy
UpdateDeploymentImage = deploy.UpdateDeploymentImage
isDagOnlyDeploy bool
description string
isImageOnlyDeploy bool
imageName string
runtimeVersionForImageName string
imagePresentOnRemote bool
ErrBothDagsOnlyAndImageOnlySet = errors.New("cannot use both --dags and --image together. Run 'astro deploy' to update both your image and dags")
ErrImageNameNotPassedForRemoteFlag = errors.New("--image-name is mandatory when --remote flag is passed")
)

var deployExample = `
Expand Down Expand Up @@ -61,6 +66,9 @@ func NewDeployCmd() *cobra.Command {
cmd.Flags().StringVar(&workspaceID, "workspace-id", "", "workspace assigned to deployment")
cmd.Flags().StringVar(&description, "description", "", "Improve traceability by attaching a description to a code deploy. If you don't provide a description, the system automatically assigns a default description based on the deploy type.")
cmd.Flags().BoolVarP(&isImageOnlyDeploy, "image", "", false, "Push only an image to your Astro Deployment. This only works for Dag-only, Git-sync-based and NFS-based deployments.")
cmd.Flags().StringVarP(&imageName, "image-name", "i", "", "Name of the custom image(should be present locally unless --remote is specified) to deploy")
cmd.Flags().StringVar(&runtimeVersionForImageName, "runtime-version", "", "Runtime version of the image to deploy. Example - 12.1.1. Mandatory if --image-name --remote is provided")
cmd.Flags().BoolVarP(&imagePresentOnRemote, "remote", "", false, "Custom image which is present on the remote registry. Can only be used with --image-name flag")

if !context.IsCloudContext() && houston.VerifyVersionMatch(houstonVersion, houston.VersionRestrictions{GTE: "0.34.0"}) {
cmd.Flags().BoolVarP(&isDagOnlyDeploy, "dags", "d", false, "Push only DAGs to your Deployment")
Expand Down Expand Up @@ -119,11 +127,22 @@ func deployAirflow(cmd *cobra.Command, args []string) error {
return DagsOnlyDeploy(houstonClient, appConfig, ws, deploymentID, config.WorkingPath, nil, true, description)
}

// Since we prompt the user to enter the deploymentID in come cases for DeployAirflowImage, reusing the same deploymentID for DagsOnlyDeploy
deploymentID, err = DeployAirflowImage(houstonClient, config.WorkingPath, deploymentID, ws, byoRegistryDomain, ignoreCacheDeploy, byoRegistryEnabled, forcePrompt, description, isImageOnlyDeploy)
if err != nil {
return err
if imagePresentOnRemote {
if imageName == "" {
return ErrImageNameNotPassedForRemoteFlag
}
deploymentID, err = UpdateDeploymentImage(houstonClient, deploymentID, ws, runtimeVersionForImageName, imageName)
if err != nil {
return err
}
} else {
// Since we prompt the user to enter the deploymentID in come cases for DeployAirflowImage, reusing the same deploymentID for DagsOnlyDeploy
deploymentID, err = DeployAirflowImage(houstonClient, config.WorkingPath, deploymentID, ws, byoRegistryDomain, ignoreCacheDeploy, byoRegistryEnabled, forcePrompt, description, isImageOnlyDeploy, imageName)
if err != nil {
return err
}
}

// Don't deploy dags even for dags-only deployments --image is passed
if isImageOnlyDeploy {
fmt.Println("Dags in the project will not be deployed since --image is passed.")
Expand Down
69 changes: 61 additions & 8 deletions cmd/software/deploy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func (s *Suite) TestDeploy() {
EnsureProjectDir = func(cmd *cobra.Command, args []string) error {
return nil
}
DeployAirflowImage = func(houstonClient houston.ClientInterface, path, deploymentID, wsID, byoRegistryDomain string, ignoreCacheDeploy, byoRegistryEnabled, prompt bool, description string, isImageOnlyDeploy bool) (string, error) {
DeployAirflowImage = func(houstonClient houston.ClientInterface, path, deploymentID, wsID, byoRegistryDomain string, ignoreCacheDeploy, byoRegistryEnabled, prompt bool, description string, isImageOnlyDeploy bool, imageName string) (string, error) {
if description == "" {
return deploymentID, fmt.Errorf("description should not be empty")
}
Expand All @@ -52,7 +52,7 @@ func (s *Suite) TestDeploy() {
s.NoError(err)

// Test when the default description is used
DeployAirflowImage = func(houstonClient houston.ClientInterface, path, deploymentID, wsID, byoRegistryDomain string, ignoreCacheDeploy, byoRegistryEnabled, prompt bool, description string, isImageOnlyDeploy bool) (string, error) {
DeployAirflowImage = func(houstonClient houston.ClientInterface, path, deploymentID, wsID, byoRegistryDomain string, ignoreCacheDeploy, byoRegistryEnabled, prompt bool, description string, isImageOnlyDeploy bool, imageName string) (string, error) {
expectedDesc := "Deployed via <astro deploy>"
if description != expectedDesc {
return deploymentID, fmt.Errorf("expected description to be '%s', but got '%s'", expectedDesc, description)
Expand All @@ -67,14 +67,14 @@ func (s *Suite) TestDeploy() {
DagsOnlyDeploy = deploy.DagsOnlyDeploy

s.Run("error should be returned for astro deploy, if DeployAirflowImage throws error", func() {
DeployAirflowImage = func(houstonClient houston.ClientInterface, path, deploymentID, wsID, byoRegistryDomain string, ignoreCacheDeploy, byoRegistryEnabled, prompt bool, description string, isImageOnlyDeploy bool) (string, error) {
DeployAirflowImage = func(houstonClient houston.ClientInterface, path, deploymentID, wsID, byoRegistryDomain string, ignoreCacheDeploy, byoRegistryEnabled, prompt bool, description string, isImageOnlyDeploy bool, imageName string) (string, error) {
return deploymentID, deploy.ErrNoWorkspaceID
}

err := execDeployCmd([]string{"-f"}...)
s.ErrorIs(err, deploy.ErrNoWorkspaceID)

DeployAirflowImage = func(houstonClient houston.ClientInterface, path, deploymentID, wsID, byoRegistryDomain string, ignoreCacheDeploy, byoRegistryEnabled, prompt bool, description string, isImageOnlyDeploy bool) (string, error) {
DeployAirflowImage = func(houstonClient houston.ClientInterface, path, deploymentID, wsID, byoRegistryDomain string, ignoreCacheDeploy, byoRegistryEnabled, prompt bool, description string, isImageOnlyDeploy bool, imageName string) (string, error) {
return deploymentID, nil
}
})
Expand All @@ -85,7 +85,6 @@ func (s *Suite) TestDeploy() {
}
err := execDeployCmd([]string{"-f"}...)
s.ErrorIs(err, deploy.ErrNoWorkspaceID)
DagsOnlyDeploy = deploy.DagsOnlyDeploy
})

s.Run("No error should be returned for astro deploy, if dags deploy throws error but the feature itself is disabled", func() {
Expand All @@ -104,15 +103,15 @@ func (s *Suite) TestDeploy() {
})

s.Run("Test for the flag --image for image deployment", func() {
DeployAirflowImage = func(houstonClient houston.ClientInterface, path, deploymentID, wsID, byoRegistryDomain string, ignoreCacheDeploy, byoRegistryEnabled, prompt bool, description string, isImageOnlyDeploy bool) (string, error) {
DeployAirflowImage = func(houstonClient houston.ClientInterface, path, deploymentID, wsID, byoRegistryDomain string, ignoreCacheDeploy, byoRegistryEnabled, prompt bool, description string, isImageOnlyDeploy bool, imageName string) (string, error) {
return deploymentID, deploy.ErrDeploymentTypeIncorrectForImageOnly
}
err := execDeployCmd([]string{"test-deployment-id", "--image", "--force"}...)
s.ErrorIs(err, deploy.ErrDeploymentTypeIncorrectForImageOnly)
})

s.Run("Test for the flag --image for dags-only deployment", func() {
DeployAirflowImage = func(houstonClient houston.ClientInterface, path, deploymentID, wsID, byoRegistryDomain string, ignoreCacheDeploy, byoRegistryEnabled, prompt bool, description string, isImageOnlyDeploy bool) (string, error) {
DeployAirflowImage = func(houstonClient houston.ClientInterface, path, deploymentID, wsID, byoRegistryDomain string, ignoreCacheDeploy, byoRegistryEnabled, prompt bool, description string, isImageOnlyDeploy bool, imageName string) (string, error) {
return deploymentID, nil
}
// This function is not called since --image is passed
Expand All @@ -123,6 +122,61 @@ func (s *Suite) TestDeploy() {
s.ErrorIs(err, nil)
})

s.Run("Test for the flag --image-name", func() {
var capturedImageName string
DeployAirflowImage = func(houstonClient houston.ClientInterface, path, deploymentID, wsID, byoRegistryDomain string, ignoreCacheDeploy, byoRegistryEnabled, prompt bool, description string, isImageOnlyDeploy bool, imageName string) (string, error) {
capturedImageName = imageName // Capture the imageName
return deploymentID, nil
}
DagsOnlyDeploy = func(houstonClient houston.ClientInterface, appConfig *houston.AppConfig, wsID, deploymentID, dagsParentPath string, dagDeployURL *string, cleanUpFiles bool, description string) error {
return nil
}
testImageName := "test-image-name" // Set the expected image name
err := execDeployCmd([]string{"test-deployment-id", "--image-name=" + testImageName, "--force", "--workspace-id=" + mockWorkspace.ID}...)

s.ErrorIs(err, nil)
s.Equal(testImageName, capturedImageName, "The imageName passed to DeployAirflowImage is incorrect")
})

s.Run("Test for the flag --image-name with --remote. Dags should be deployed but DeployAirflowImage shouldn't be called", func() {
DagsOnlyDeploy = func(houstonClient houston.ClientInterface, appConfig *houston.AppConfig, wsID, deploymentID, dagsParentPath string, dagDeployURL *string, cleanUpFiles bool, description string) error {
return nil
}
// Create a flag to track if DeployAirflowImage is called
deployAirflowImageCalled := false

// Mock function for DeployAirflowImage
DeployAirflowImage = func(houstonClient houston.ClientInterface, path, deploymentID, wsID, byoRegistryDomain string, ignoreCacheDeploy, byoRegistryEnabled, prompt bool, description string, isImageOnlyDeploy bool, imageName string) (string, error) {
deployAirflowImageCalled = true // Set the flag if this function is called
return deploymentID, nil
}
UpdateDeploymentImage = func(houstonClient houston.ClientInterface, deploymentID, wsID, runtimeVersion, imageName string) (string, error) {
return "", nil
}
testImageName := "test-image-name" // Set the expected image name
err := execDeployCmd([]string{"test-deployment-id", "--image-name=" + testImageName, "--force", "--remote", "--workspace-id=" + mockWorkspace.ID}...)
rujhan-arora-astronomer marked this conversation as resolved.
Show resolved Hide resolved
s.ErrorIs(err, nil)
// Assert that DeployAirflowImage was NOT called
s.False(deployAirflowImageCalled, "DeployAirflowImage should not be called when --remote is specified")
})

s.Run("Test for the flag --image-name with --remote. Dags should not be deployed if UpdateDeploymentImage throws an error", func() {
UpdateDeploymentImage = func(houstonClient houston.ClientInterface, deploymentID, wsID, runtimeVersion, imageName string) (string, error) {
return "", errNoWorkspaceFound
}
testImageName := "test-image-name" // Set the expected image name
err := execDeployCmd([]string{"test-deployment-id", "--image-name=" + testImageName, "--force", "--remote", "--workspace-id=" + mockWorkspace.ID}...)
s.ErrorIs(err, errNoWorkspaceFound)
})

s.Run("Test for the flag --remote without --image-name. It should throw an error", func() {
UpdateDeploymentImage = func(houstonClient houston.ClientInterface, deploymentID, wsID, runtimeVersion, imageName string) (string, error) {
return "", errNoWorkspaceFound
}
err := execDeployCmd([]string{"test-deployment-id", "--force", "--remote", "--workspace-id=" + mockWorkspace.ID}...)
s.ErrorIs(err, ErrImageNameNotPassedForRemoteFlag)
})

s.Run("error should be returned if BYORegistryEnabled is true but BYORegistryDomain is empty", func() {
appConfig = &houston.AppConfig{
BYORegistryDomain: "",
Expand All @@ -135,6 +189,5 @@ func (s *Suite) TestDeploy() {
}
err := execDeployCmd([]string{"-f"}...)
s.ErrorIs(err, deploy.ErrBYORegistryDomainNotSet)
DagsOnlyDeploy = deploy.DagsOnlyDeploy
})
}
16 changes: 14 additions & 2 deletions cmd/software/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"testing"

testUtil "github.com/astronomer/astro-cli/pkg/testing"
"github.com/astronomer/astro-cli/software/deploy"
"github.com/stretchr/testify/suite"
)

Expand All @@ -22,9 +23,20 @@ func (s *Suite) SetupSuite() {
func (s *Suite) SetupTest() {
// Reset the version once this is torn down
houstonVersion = "0.34.0"
DagsOnlyDeploy = deploy.DagsOnlyDeploy
}

func (s *Suite) SetupSubTest() {
DagsOnlyDeploy = deploy.DagsOnlyDeploy
}

func (s *Suite) TearDownSuite() {
DagsOnlyDeploy = deploy.DagsOnlyDeploy
UpdateDeploymentImage = deploy.UpdateDeploymentImage
}

var (
_ suite.SetupAllSuite = (*Suite)(nil)
_ suite.SetupTestSuite = (*Suite)(nil)
_ suite.SetupAllSuite = (*Suite)(nil)
_ suite.SetupTestSuite = (*Suite)(nil)
_ suite.TearDownAllSuite = (*Suite)(nil)
)
5 changes: 5 additions & 0 deletions houston/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,18 @@ package houston
import (
"testing"

testUtil "github.com/astronomer/astro-cli/pkg/testing"
"github.com/stretchr/testify/suite"
)

type Suite struct {
suite.Suite
}

func SetupSuite() {
testUtil.InitTestConfig(testUtil.SoftwarePlatform)
}

func TestHouston(t *testing.T) {
suite.Run(t, new(Suite))
}
Loading