github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/worker/caasoperator/initializer_test.go (about)

     1  // Copyright 2019 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package caasoperator_test
     5  
     6  import (
     7  	"bytes"
     8  	"os"
     9  	"path/filepath"
    10  	"time"
    11  
    12  	jujuclock "github.com/juju/clock"
    13  	"github.com/juju/clock/testclock"
    14  	"github.com/juju/errors"
    15  	"github.com/juju/loggo"
    16  	"github.com/juju/names/v5"
    17  	jc "github.com/juju/testing/checkers"
    18  	"go.uber.org/mock/gomock"
    19  	gc "gopkg.in/check.v1"
    20  
    21  	"github.com/juju/juju/caas"
    22  	"github.com/juju/juju/caas/kubernetes/provider/exec"
    23  	"github.com/juju/juju/testing"
    24  	coretesting "github.com/juju/juju/testing"
    25  	"github.com/juju/juju/worker/caasoperator"
    26  	"github.com/juju/juju/worker/caasoperator/mocks"
    27  )
    28  
    29  type UnitInitializerSuite struct {
    30  	coretesting.BaseSuite
    31  }
    32  
    33  var _ = gc.Suite(&UnitInitializerSuite{})
    34  
    35  func (s *UnitInitializerSuite) SetUpTest(c *gc.C) {
    36  	s.BaseSuite.SetUpTest(c)
    37  }
    38  
    39  func (s *UnitInitializerSuite) TestInitialize(c *gc.C) {
    40  	ctrl := gomock.NewController(c)
    41  	defer ctrl.Finish()
    42  
    43  	mockExecClient := mocks.NewMockExecutor(ctrl)
    44  
    45  	params := caasoperator.InitializeUnitParams{
    46  		ReTrier: func(f func() error, _ func(error) bool, _ caasoperator.Logger, _ jujuclock.Clock, _ <-chan struct{}) error {
    47  			return f()
    48  		},
    49  		UnitTag: names.NewUnitTag("gitlab/0"),
    50  		Logger:  loggo.GetLogger("test"),
    51  		Paths: caasoperator.Paths{
    52  			State: caasoperator.StatePaths{
    53  				CharmDir: "dir/charm",
    54  			},
    55  		},
    56  		ExecClient: mockExecClient,
    57  		OperatorInfo: caas.OperatorInfo{
    58  			CACert: "ca-cert",
    59  		},
    60  		ProviderID: "gitlab-ffff",
    61  		TempDir: func(dir string, prefix string) (string, error) {
    62  			return filepath.Join(dir, prefix+"-random"), nil
    63  		},
    64  		WriteFile: func(path string, data []byte, perm os.FileMode) error {
    65  			return nil
    66  		},
    67  	}
    68  
    69  	gomock.InOrder(
    70  		mockExecClient.EXPECT().Exec(exec.ExecParams{
    71  			Commands:      []string{"mkdir", "-p", filepath.Join(os.TempDir(), "unit-gitlab-0-random")},
    72  			PodName:       "gitlab-ffff",
    73  			ContainerName: "juju-pod-init",
    74  			Stdout:        &bytes.Buffer{},
    75  			Stderr:        &bytes.Buffer{},
    76  		}, gomock.Any()).Return(nil),
    77  		mockExecClient.EXPECT().Copy(exec.CopyParams{
    78  			Src: exec.FileResource{
    79  				Path: "dir/charm",
    80  			},
    81  			Dest: exec.FileResource{
    82  				Path:          filepath.Join(os.TempDir(), "unit-gitlab-0-random"),
    83  				PodName:       "gitlab-ffff",
    84  				ContainerName: "juju-pod-init",
    85  			},
    86  		}, gomock.Any()).Return(nil),
    87  		mockExecClient.EXPECT().Copy(exec.CopyParams{
    88  			Src: exec.FileResource{
    89  				Path: filepath.Join(os.TempDir(), "unit-gitlab-0-random/ca.crt"),
    90  			},
    91  			Dest: exec.FileResource{
    92  				Path:          filepath.Join(os.TempDir(), "unit-gitlab-0-random/ca.crt"),
    93  				PodName:       "gitlab-ffff",
    94  				ContainerName: "juju-pod-init",
    95  			},
    96  		}, gomock.Any()).Return(nil),
    97  		mockExecClient.EXPECT().Copy(exec.CopyParams{
    98  			Src: exec.FileResource{
    99  				Path: "/var/lib/juju/agents/unit-gitlab-0/operator-client-cache.yaml",
   100  			},
   101  			Dest: exec.FileResource{
   102  				Path:          filepath.Join(os.TempDir(), "unit-gitlab-0-random/operator-client-cache.yaml"),
   103  				PodName:       "gitlab-ffff",
   104  				ContainerName: "juju-pod-init",
   105  			},
   106  		}, gomock.Any()).Return(nil),
   107  		mockExecClient.EXPECT().Exec(exec.ExecParams{
   108  			Commands: []string{"/var/lib/juju/tools/jujud", "caas-unit-init",
   109  				"--unit", "unit-gitlab-0",
   110  				"--charm-dir",
   111  				filepath.Join(os.TempDir(), "unit-gitlab-0-random/charm"),
   112  				"--send",
   113  				"--operator-file",
   114  				filepath.Join(os.TempDir(), "unit-gitlab-0-random/operator-client-cache.yaml"),
   115  				"--operator-ca-cert-file",
   116  				filepath.Join(os.TempDir(), "unit-gitlab-0-random/ca.crt"),
   117  			},
   118  			WorkingDir:    "/var/lib/juju",
   119  			PodName:       "gitlab-ffff",
   120  			ContainerName: "juju-pod-init",
   121  			Stdout:        &bytes.Buffer{},
   122  			Stderr:        &bytes.Buffer{},
   123  		}, gomock.Any()).Return(nil),
   124  	)
   125  
   126  	cancel := make(chan struct{})
   127  	err := caasoperator.InitializeUnit(params, cancel)
   128  	c.Assert(err, jc.ErrorIsNil)
   129  }
   130  
   131  func (s *UnitInitializerSuite) TestInitializeUnitMissingProviderID(c *gc.C) {
   132  	ctrl := gomock.NewController(c)
   133  	defer ctrl.Finish()
   134  
   135  	mockExecClient := mocks.NewMockExecutor(ctrl)
   136  
   137  	params := caasoperator.InitializeUnitParams{
   138  		ReTrier: func(f func() error, _ func(error) bool, _ caasoperator.Logger, _ jujuclock.Clock, _ <-chan struct{}) error {
   139  			return f()
   140  		},
   141  		UnitTag: names.NewUnitTag("gitlab/0"),
   142  		Logger:  loggo.GetLogger("test"),
   143  		Paths: caasoperator.Paths{
   144  			State: caasoperator.StatePaths{
   145  				CharmDir: "dir/charm",
   146  			},
   147  		},
   148  		ExecClient: mockExecClient,
   149  		OperatorInfo: caas.OperatorInfo{
   150  			CACert: "ca-cert",
   151  		},
   152  		ProviderID: "",
   153  		TempDir: func(dir string, prefix string) (string, error) {
   154  			return filepath.Join(dir, prefix+"-random"), nil
   155  		},
   156  		WriteFile: func(path string, data []byte, perm os.FileMode) error {
   157  			return nil
   158  		},
   159  	}
   160  
   161  	gomock.InOrder()
   162  
   163  	cancel := make(chan struct{})
   164  	err := caasoperator.InitializeUnit(params, cancel)
   165  	c.Assert(err, gc.ErrorMatches, "missing ProviderID not valid")
   166  }
   167  
   168  func (s *UnitInitializerSuite) TestInitializeContainerMissing(c *gc.C) {
   169  	ctrl := gomock.NewController(c)
   170  	defer ctrl.Finish()
   171  
   172  	mockExecClient := mocks.NewMockExecutor(ctrl)
   173  
   174  	params := caasoperator.InitializeUnitParams{
   175  		ReTrier: func(f func() error, _ func(error) bool, _ caasoperator.Logger, _ jujuclock.Clock, _ <-chan struct{}) error {
   176  			return f()
   177  		},
   178  		UnitTag: names.NewUnitTag("gitlab/0"),
   179  		Logger:  loggo.GetLogger("test"),
   180  		Paths: caasoperator.Paths{
   181  			State: caasoperator.StatePaths{
   182  				CharmDir: "dir/charm",
   183  			},
   184  		},
   185  		ExecClient: mockExecClient,
   186  		OperatorInfo: caas.OperatorInfo{
   187  			CACert: "ca-cert",
   188  		},
   189  		ProviderID: "gitlab-ffff",
   190  		TempDir: func(dir string, prefix string) (string, error) {
   191  			return filepath.Join(dir, prefix+"-random"), nil
   192  		},
   193  		WriteFile: func(path string, data []byte, perm os.FileMode) error {
   194  			return nil
   195  		},
   196  	}
   197  
   198  	gomock.InOrder(
   199  		mockExecClient.EXPECT().Exec(exec.ExecParams{
   200  			Commands:      []string{"mkdir", "-p", filepath.Join(os.TempDir(), "unit-gitlab-0-random")},
   201  			PodName:       "gitlab-ffff",
   202  			ContainerName: "juju-pod-init",
   203  			Stdout:        &bytes.Buffer{},
   204  			Stderr:        &bytes.Buffer{},
   205  		}, gomock.Any()).Return(nil),
   206  		mockExecClient.EXPECT().Copy(exec.CopyParams{
   207  			Src: exec.FileResource{
   208  				Path: "dir/charm",
   209  			},
   210  			Dest: exec.FileResource{
   211  				Path:          filepath.Join(os.TempDir(), "unit-gitlab-0-random"),
   212  				PodName:       "gitlab-ffff",
   213  				ContainerName: "juju-pod-init",
   214  			},
   215  		}, gomock.Any()).Return(errors.NotFoundf("container")),
   216  	)
   217  
   218  	cancel := make(chan struct{})
   219  	err := caasoperator.InitializeUnit(params, cancel)
   220  	c.Assert(err, gc.ErrorMatches, "container not found")
   221  }
   222  
   223  func (s *UnitInitializerSuite) TestInitializePodNotFound(c *gc.C) {
   224  	ctrl := gomock.NewController(c)
   225  	defer ctrl.Finish()
   226  
   227  	mockExecClient := mocks.NewMockExecutor(ctrl)
   228  
   229  	params := caasoperator.InitializeUnitParams{
   230  		ReTrier: func(f func() error, _ func(error) bool, _ caasoperator.Logger, _ jujuclock.Clock, _ <-chan struct{}) error {
   231  			return f()
   232  		},
   233  		UnitTag: names.NewUnitTag("gitlab/0"),
   234  		Logger:  loggo.GetLogger("test"),
   235  		Paths: caasoperator.Paths{
   236  			State: caasoperator.StatePaths{
   237  				CharmDir: "dir/charm",
   238  			},
   239  		},
   240  		ExecClient: mockExecClient,
   241  		OperatorInfo: caas.OperatorInfo{
   242  			CACert: "ca-cert",
   243  		},
   244  		ProviderID: "gitlab-ffff",
   245  		TempDir: func(dir string, prefix string) (string, error) {
   246  			return filepath.Join(dir, prefix+"-random"), nil
   247  		},
   248  		WriteFile: func(path string, data []byte, perm os.FileMode) error {
   249  			return nil
   250  		},
   251  	}
   252  
   253  	gomock.InOrder(
   254  		mockExecClient.EXPECT().Exec(exec.ExecParams{
   255  			Commands:      []string{"mkdir", "-p", filepath.Join(os.TempDir(), "unit-gitlab-0-random")},
   256  			PodName:       "gitlab-ffff",
   257  			ContainerName: "juju-pod-init",
   258  			Stdout:        &bytes.Buffer{},
   259  			Stderr:        &bytes.Buffer{},
   260  		}, gomock.Any()).Return(nil),
   261  		mockExecClient.EXPECT().Copy(exec.CopyParams{
   262  			Src: exec.FileResource{
   263  				Path: "dir/charm",
   264  			},
   265  			Dest: exec.FileResource{
   266  				Path:          filepath.Join(os.TempDir(), "unit-gitlab-0-random"),
   267  				PodName:       "gitlab-ffff",
   268  				ContainerName: "juju-pod-init",
   269  			},
   270  		}, gomock.Any()).Return(errors.NotFoundf("container")),
   271  	)
   272  
   273  	cancel := make(chan struct{})
   274  	err := caasoperator.InitializeUnit(params, cancel)
   275  	c.Assert(err, gc.ErrorMatches, "container not found")
   276  }
   277  
   278  func (s *UnitInitializerSuite) TestRunnerWithRetry(c *gc.C) {
   279  	cancel := make(chan struct{})
   280  	clk := testclock.NewClock(time.Time{})
   281  	called := 0
   282  	execRequest := func() error {
   283  		called++
   284  		if called < 3 {
   285  			return exec.NewExecRetryableError(errors.New("fake testing 137"))
   286  		}
   287  		return nil
   288  	}
   289  
   290  	errChan := make(chan error)
   291  	go func() {
   292  		errChan <- caasoperator.RunnerWithRetry(execRequest, func(err error) bool {
   293  			return err != nil && !exec.IsExecRetryableError(err)
   294  		}, loggo.GetLogger("test"), clk, cancel)
   295  	}()
   296  	err := clk.WaitAdvance(2*time.Second, testing.ShortWait, 1)
   297  	c.Assert(err, jc.ErrorIsNil)
   298  	err = clk.WaitAdvance(2*time.Second, testing.ShortWait, 1)
   299  	c.Assert(err, jc.ErrorIsNil)
   300  
   301  	select {
   302  	case err := <-errChan:
   303  		c.Assert(err, jc.ErrorIsNil)
   304  		c.Assert(called, gc.DeepEquals, 3)
   305  	case <-time.After(testing.LongWait):
   306  		c.Fatalf("timed out waiting for RunnerWithRetry return")
   307  	}
   308  }