github.com/splunk/dan1-qbec@v0.7.3/internal/commands/apply_test.go (about)

     1  /*
     2     Copyright 2019 Splunk Inc.
     3  
     4     Licensed under the Apache License, Version 2.0 (the "License");
     5     you may not use this file except in compliance with the License.
     6     You may obtain a copy of the License at
     7  
     8         http://www.apache.org/licenses/LICENSE-2.0
     9  
    10     Unless required by applicable law or agreed to in writing, software
    11     distributed under the License is distributed on an "AS IS" BASIS,
    12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13     See the License for the specific language governing permissions and
    14     limitations under the License.
    15  */
    16  
    17  package commands
    18  
    19  import (
    20  	"regexp"
    21  	"testing"
    22  
    23  	"github.com/splunk/qbec/internal/model"
    24  	"github.com/splunk/qbec/internal/remote"
    25  	"github.com/splunk/qbec/internal/rollout"
    26  	"github.com/stretchr/testify/assert"
    27  	"github.com/stretchr/testify/require"
    28  )
    29  
    30  func TestNsWrap(t *testing.T) {
    31  	obj := model.NewK8sObject(map[string]interface{}{
    32  		"kind":       "foo",
    33  		"apiversion": "apps/v1",
    34  		"metadata": map[string]interface{}{
    35  			"namespace": "foo",
    36  			"name":      "foo",
    37  		},
    38  	})
    39  	w := nsWrap{K8sMeta: obj, ns: "bar"}
    40  	a := assert.New(t)
    41  	a.Equal("foo", w.GetNamespace())
    42  	a.Equal("foo", w.GetName())
    43  	obj = model.NewK8sObject(map[string]interface{}{
    44  		"kind":       "foo",
    45  		"apiversion": "apps/v1",
    46  		"metadata": map[string]interface{}{
    47  			"name": "foo",
    48  		},
    49  	})
    50  	w = nsWrap{K8sMeta: obj, ns: "bar"}
    51  	a.Equal("bar", w.GetNamespace())
    52  	a.Equal("foo", w.GetName())
    53  }
    54  
    55  func TestApplyBasic(t *testing.T) {
    56  	s := newScaffold(t)
    57  	defer s.reset()
    58  	applyWaitFn = func(objects []model.K8sMeta, wp rollout.WatchProvider, opts rollout.WaitOptions) (finalErr error) {
    59  		return nil
    60  	}
    61  	first := true
    62  	var captured remote.SyncOptions
    63  	s.client.syncFunc = func(obj model.K8sLocalObject, opts remote.SyncOptions) (*remote.SyncResult, error) {
    64  		if first {
    65  			first = false
    66  			captured = opts
    67  		}
    68  		switch {
    69  		case obj.GetName() == "svc2-cm":
    70  			return &remote.SyncResult{Type: remote.SyncUpdated, Details: "data updated"}, nil
    71  		case obj.GetName() == "svc2-secret":
    72  			return &remote.SyncResult{Type: remote.SyncCreated, Details: "some yaml"}, nil
    73  		case obj.GetName() == "svc2-deploy":
    74  			return &remote.SyncResult{Type: remote.SyncObjectsIdentical, Details: "sync skipped"}, nil
    75  		case obj.GetName() == "":
    76  			return &remote.SyncResult{Type: remote.SyncCreated, GeneratedName: obj.GetGenerateName() + "1234", Details: "created"}, nil
    77  		default:
    78  			return &remote.SyncResult{Type: remote.SyncObjectsIdentical, Details: "sync skipped"}, nil
    79  		}
    80  	}
    81  	s.client.listFunc = stdLister
    82  	s.client.deleteFunc = func(obj model.K8sMeta, dryRun bool) (*remote.SyncResult, error) {
    83  		return &remote.SyncResult{Type: remote.SyncDeleted}, nil
    84  	}
    85  	err := s.executeCommand("apply", "dev", "--wait")
    86  	require.Nil(t, err)
    87  	stats := s.outputStats()
    88  	a := assert.New(t)
    89  	a.EqualValues(remote.SyncOptions{}, captured)
    90  	a.True(stats["same"].(float64) > 0)
    91  	a.EqualValues(8, stats["same"])
    92  	a.EqualValues([]interface{}{"Secret:bar-system:svc2-secret", "Job::tj-1234"}, stats["created"])
    93  	a.EqualValues([]interface{}{"ConfigMap:bar-system:svc2-cm"}, stats["updated"])
    94  	a.EqualValues([]interface{}{"Deployment:bar-system:svc2-previous-deploy"}, stats["deleted"])
    95  	s.assertErrorLineMatch(regexp.MustCompile(`sync ConfigMap:bar-system:svc2-cm`))
    96  }
    97  
    98  func TestApplyFlags(t *testing.T) {
    99  	s := newScaffold(t)
   100  	defer s.reset()
   101  	first := true
   102  	var captured remote.SyncOptions
   103  	s.client.syncFunc = func(obj model.K8sLocalObject, opts remote.SyncOptions) (*remote.SyncResult, error) {
   104  		if first {
   105  			first = false
   106  			captured = opts
   107  		}
   108  		switch {
   109  		case obj.GetName() == "svc2-cm":
   110  			return &remote.SyncResult{Type: remote.SyncUpdated, Details: "data updated"}, nil
   111  		case obj.GetName() == "svc2-secret":
   112  			return &remote.SyncResult{Type: remote.SyncSkip, Details: "create skipped"}, nil
   113  		default:
   114  			return &remote.SyncResult{Type: remote.SyncObjectsIdentical, Details: "sync skipped"}, nil
   115  		}
   116  	}
   117  	err := s.executeCommand("apply", "dev", "-S", "-n", "--skip-create", "--gc=false")
   118  	require.Nil(t, err)
   119  	stats := s.outputStats()
   120  	a := assert.New(t)
   121  	a.EqualValues(remote.SyncOptions{DryRun: true, ShowSecrets: true, DisableCreate: true}, captured)
   122  	a.EqualValues(nil, stats["created"])
   123  	a.EqualValues([]interface{}{"Secret:bar-system:svc2-secret"}, stats["skipped"])
   124  	a.EqualValues([]interface{}{"ConfigMap:bar-system:svc2-cm"}, stats["updated"])
   125  	s.assertErrorLineMatch(regexp.MustCompile(`\[dry-run\] sync ConfigMap:bar-system:svc2-cm`))
   126  	s.assertErrorLineMatch(regexp.MustCompile(`\*\* dry-run mode, nothing was actually changed \*\*`))
   127  }
   128  
   129  func TestApplyNegative(t *testing.T) {
   130  	tests := []struct {
   131  		name     string
   132  		args     []string
   133  		asserter func(s *scaffold, err error)
   134  	}{
   135  		{
   136  			name: "no env",
   137  			args: []string{"apply"},
   138  			asserter: func(s *scaffold, err error) {
   139  				a := assert.New(s.t)
   140  				a.True(isUsageError(err))
   141  				a.Equal("exactly one environment required", err.Error())
   142  			},
   143  		},
   144  		{
   145  			name: "2 envs",
   146  			args: []string{"apply", "dev", "prod"},
   147  			asserter: func(s *scaffold, err error) {
   148  				a := assert.New(s.t)
   149  				a.True(isUsageError(err))
   150  				a.Equal("exactly one environment required", err.Error())
   151  			},
   152  		},
   153  		{
   154  			name: "bad env",
   155  			args: []string{"apply", "foo"},
   156  			asserter: func(s *scaffold, err error) {
   157  				a := assert.New(s.t)
   158  				a.False(isUsageError(err))
   159  				a.Equal("invalid environment \"foo\"", err.Error())
   160  			},
   161  		},
   162  		{
   163  			name: "baseline env",
   164  			args: []string{"apply", "_"},
   165  			asserter: func(s *scaffold, err error) {
   166  				a := assert.New(s.t)
   167  				a.True(isUsageError(err))
   168  				a.Equal("cannot apply baseline environment, use a real environment", err.Error())
   169  			},
   170  		},
   171  		{
   172  			name: "c and C",
   173  			args: []string{"apply", "dev", "-c", "cluster-objects", "-C", "service2"},
   174  			asserter: func(s *scaffold, err error) {
   175  				a := assert.New(s.t)
   176  				a.True(isUsageError(err))
   177  				a.Equal(`cannot include as well as exclude components, specify one or the other`, err.Error())
   178  			},
   179  		},
   180  		{
   181  			name: "k and K",
   182  			args: []string{"apply", "dev", "-k", "namespace", "-K", "secret"},
   183  			asserter: func(s *scaffold, err error) {
   184  				a := assert.New(s.t)
   185  				a.True(isUsageError(err))
   186  				a.Equal(`cannot include as well as exclude kinds, specify one or the other`, err.Error())
   187  			},
   188  		},
   189  	}
   190  	for _, test := range tests {
   191  		t.Run(test.name, func(t *testing.T) {
   192  			s := newScaffold(t)
   193  			defer s.reset()
   194  			err := s.executeCommand(test.args...)
   195  			require.NotNil(t, err)
   196  			test.asserter(s, err)
   197  		})
   198  	}
   199  
   200  }