github.com/databricks/cli@v0.203.0/bundle/config/mutator/process_environment_mode_test.go (about)

     1  package mutator
     2  
     3  import (
     4  	"context"
     5  	"reflect"
     6  	"strings"
     7  	"testing"
     8  
     9  	"github.com/databricks/cli/bundle"
    10  	"github.com/databricks/cli/bundle/config"
    11  	"github.com/databricks/cli/bundle/config/resources"
    12  	"github.com/databricks/databricks-sdk-go/service/iam"
    13  	"github.com/databricks/databricks-sdk-go/service/jobs"
    14  	"github.com/databricks/databricks-sdk-go/service/ml"
    15  	"github.com/databricks/databricks-sdk-go/service/pipelines"
    16  	"github.com/stretchr/testify/assert"
    17  	"github.com/stretchr/testify/require"
    18  )
    19  
    20  func mockBundle(mode config.Mode) *bundle.Bundle {
    21  	return &bundle.Bundle{
    22  		Config: config.Root{
    23  			Bundle: config.Bundle{
    24  				Mode: mode,
    25  				Git: config.Git{
    26  					OriginURL: "http://origin",
    27  					Branch:    "main",
    28  				},
    29  			},
    30  			Workspace: config.Workspace{
    31  				CurrentUser: &config.User{
    32  					ShortName: "lennart",
    33  					User: &iam.User{
    34  						UserName: "lennart@company.com",
    35  						Id:       "1",
    36  					},
    37  				},
    38  				StatePath:     "/Users/lennart@company.com/.bundle/x/y/state",
    39  				ArtifactsPath: "/Users/lennart@company.com/.bundle/x/y/artifacts",
    40  				FilesPath:     "/Users/lennart@company.com/.bundle/x/y/files",
    41  			},
    42  			Resources: config.Resources{
    43  				Jobs: map[string]*resources.Job{
    44  					"job1": {JobSettings: &jobs.JobSettings{Name: "job1"}},
    45  				},
    46  				Pipelines: map[string]*resources.Pipeline{
    47  					"pipeline1": {PipelineSpec: &pipelines.PipelineSpec{Name: "pipeline1"}},
    48  				},
    49  				Experiments: map[string]*resources.MlflowExperiment{
    50  					"experiment1": {Experiment: &ml.Experiment{Name: "/Users/lennart.kats@databricks.com/experiment1"}},
    51  					"experiment2": {Experiment: &ml.Experiment{Name: "experiment2"}},
    52  				},
    53  				Models: map[string]*resources.MlflowModel{
    54  					"model1": {Model: &ml.Model{Name: "model1"}},
    55  				},
    56  			},
    57  		},
    58  	}
    59  }
    60  
    61  func TestProcessEnvironmentModeDevelopment(t *testing.T) {
    62  	bundle := mockBundle(config.Development)
    63  
    64  	m := ProcessEnvironmentMode()
    65  	err := m.Apply(context.Background(), bundle)
    66  	require.NoError(t, err)
    67  	assert.Equal(t, "[dev lennart] job1", bundle.Config.Resources.Jobs["job1"].Name)
    68  	assert.Equal(t, "[dev lennart] pipeline1", bundle.Config.Resources.Pipelines["pipeline1"].Name)
    69  	assert.Equal(t, "/Users/lennart.kats@databricks.com/[dev lennart] experiment1", bundle.Config.Resources.Experiments["experiment1"].Name)
    70  	assert.Equal(t, "[dev lennart] experiment2", bundle.Config.Resources.Experiments["experiment2"].Name)
    71  	assert.Equal(t, "[dev lennart] model1", bundle.Config.Resources.Models["model1"].Name)
    72  	assert.Equal(t, "dev", bundle.Config.Resources.Experiments["experiment1"].Experiment.Tags[0].Key)
    73  	assert.True(t, bundle.Config.Resources.Pipelines["pipeline1"].PipelineSpec.Development)
    74  }
    75  
    76  func TestProcessEnvironmentModeDefault(t *testing.T) {
    77  	bundle := mockBundle("")
    78  
    79  	m := ProcessEnvironmentMode()
    80  	err := m.Apply(context.Background(), bundle)
    81  	require.NoError(t, err)
    82  	assert.Equal(t, "job1", bundle.Config.Resources.Jobs["job1"].Name)
    83  	assert.Equal(t, "pipeline1", bundle.Config.Resources.Pipelines["pipeline1"].Name)
    84  	assert.False(t, bundle.Config.Resources.Pipelines["pipeline1"].PipelineSpec.Development)
    85  }
    86  
    87  func TestProcessEnvironmentModeProduction(t *testing.T) {
    88  	bundle := mockBundle(config.Production)
    89  
    90  	err := validateProductionMode(context.Background(), bundle, false)
    91  	require.ErrorContains(t, err, "state_path")
    92  
    93  	bundle.Config.Workspace.StatePath = "/Shared/.bundle/x/y/state"
    94  	bundle.Config.Workspace.ArtifactsPath = "/Shared/.bundle/x/y/artifacts"
    95  	bundle.Config.Workspace.FilesPath = "/Shared/.bundle/x/y/files"
    96  
    97  	err = validateProductionMode(context.Background(), bundle, false)
    98  	require.ErrorContains(t, err, "production")
    99  
   100  	permissions := []resources.Permission{
   101  		{
   102  			Level:    "CAN_MANAGE",
   103  			UserName: "user@company.com",
   104  		},
   105  	}
   106  	bundle.Config.Resources.Jobs["job1"].Permissions = permissions
   107  	bundle.Config.Resources.Jobs["job1"].RunAs = &jobs.JobRunAs{UserName: "user@company.com"}
   108  	bundle.Config.Resources.Pipelines["pipeline1"].Permissions = permissions
   109  	bundle.Config.Resources.Experiments["experiment1"].Permissions = permissions
   110  	bundle.Config.Resources.Experiments["experiment2"].Permissions = permissions
   111  	bundle.Config.Resources.Models["model1"].Permissions = permissions
   112  
   113  	err = validateProductionMode(context.Background(), bundle, false)
   114  	require.NoError(t, err)
   115  
   116  	assert.Equal(t, "job1", bundle.Config.Resources.Jobs["job1"].Name)
   117  	assert.Equal(t, "pipeline1", bundle.Config.Resources.Pipelines["pipeline1"].Name)
   118  	assert.False(t, bundle.Config.Resources.Pipelines["pipeline1"].PipelineSpec.Development)
   119  }
   120  
   121  func TestProcessEnvironmentModeProductionGit(t *testing.T) {
   122  	bundle := mockBundle(config.Production)
   123  
   124  	// Pretend the user didn't set Git configuration explicitly
   125  	bundle.Config.Bundle.Git.Inferred = true
   126  
   127  	err := validateProductionMode(context.Background(), bundle, false)
   128  	require.ErrorContains(t, err, "git")
   129  	bundle.Config.Bundle.Git.Inferred = false
   130  }
   131  
   132  func TestProcessEnvironmentModeProductionOkForPrincipal(t *testing.T) {
   133  	bundle := mockBundle(config.Production)
   134  
   135  	// Our environment has all kinds of problems when not using service principals ...
   136  	err := validateProductionMode(context.Background(), bundle, false)
   137  	require.Error(t, err)
   138  
   139  	// ... but we're much less strict when a principal is used
   140  	err = validateProductionMode(context.Background(), bundle, true)
   141  	require.NoError(t, err)
   142  }
   143  
   144  // Make sure that we have test coverage for all resource types
   145  func TestAllResourcesMocked(t *testing.T) {
   146  	bundle := mockBundle(config.Development)
   147  	resources := reflect.ValueOf(bundle.Config.Resources)
   148  
   149  	for i := 0; i < resources.NumField(); i++ {
   150  		field := resources.Field(i)
   151  		if field.Kind() == reflect.Map {
   152  			assert.True(
   153  				t,
   154  				!field.IsNil() && field.Len() > 0,
   155  				"process_environment_mode should support '%s' (please add it to process_environment_mode.go and extend the test suite)",
   156  				resources.Type().Field(i).Name,
   157  			)
   158  		}
   159  	}
   160  }
   161  
   162  // Make sure that we at least rename all resources
   163  func TestAllResourcesRenamed(t *testing.T) {
   164  	bundle := mockBundle(config.Development)
   165  	resources := reflect.ValueOf(bundle.Config.Resources)
   166  
   167  	m := ProcessEnvironmentMode()
   168  	err := m.Apply(context.Background(), bundle)
   169  	require.NoError(t, err)
   170  
   171  	for i := 0; i < resources.NumField(); i++ {
   172  		field := resources.Field(i)
   173  
   174  		if field.Kind() == reflect.Map {
   175  			for _, key := range field.MapKeys() {
   176  				resource := field.MapIndex(key)
   177  				nameField := resource.Elem().FieldByName("Name")
   178  				if nameField.IsValid() && nameField.Kind() == reflect.String {
   179  					assert.True(
   180  						t,
   181  						strings.Contains(nameField.String(), "dev"),
   182  						"process_environment_mode should rename '%s' in '%s'",
   183  						key,
   184  						resources.Type().Field(i).Name,
   185  					)
   186  				}
   187  			}
   188  		}
   189  	}
   190  }