github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/apiserver/facades/controller/caasoperatorprovisioner/provisioner_test.go (about)

     1  // Copyright 2017 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package caasoperatorprovisioner_test
     5  
     6  import (
     7  	"crypto/x509"
     8  
     9  	"github.com/juju/charm/v12"
    10  	"github.com/juju/errors"
    11  	"github.com/juju/names/v5"
    12  	jc "github.com/juju/testing/checkers"
    13  	"github.com/juju/version/v2"
    14  	gc "gopkg.in/check.v1"
    15  
    16  	"github.com/juju/juju/apiserver/common"
    17  	"github.com/juju/juju/apiserver/facades/controller/caasoperatorprovisioner"
    18  	apiservertesting "github.com/juju/juju/apiserver/testing"
    19  	"github.com/juju/juju/core/life"
    20  	"github.com/juju/juju/pki"
    21  	"github.com/juju/juju/rpc/params"
    22  	"github.com/juju/juju/state"
    23  	coretesting "github.com/juju/juju/testing"
    24  	jujuversion "github.com/juju/juju/version"
    25  )
    26  
    27  var _ = gc.Suite(&CAASProvisionerSuite{})
    28  
    29  type CAASProvisionerSuite struct {
    30  	coretesting.BaseSuite
    31  
    32  	resources          *common.Resources
    33  	authorizer         *apiservertesting.FakeAuthorizer
    34  	api                *caasoperatorprovisioner.API
    35  	st                 *mockState
    36  	storagePoolManager *mockStoragePoolManager
    37  	registry           *mockStorageRegistry
    38  }
    39  
    40  func (s *CAASProvisionerSuite) SetUpTest(c *gc.C) {
    41  	s.BaseSuite.SetUpTest(c)
    42  
    43  	s.resources = common.NewResources()
    44  	s.AddCleanup(func(_ *gc.C) { s.resources.StopAll() })
    45  	s.PatchValue(&jujuversion.OfficialBuild, 0)
    46  
    47  	s.authorizer = &apiservertesting.FakeAuthorizer{
    48  		Tag:        names.NewMachineTag("0"),
    49  		Controller: true,
    50  	}
    51  
    52  	s.st = newMockState()
    53  	s.storagePoolManager = &mockStoragePoolManager{}
    54  	s.registry = &mockStorageRegistry{}
    55  	api, err := caasoperatorprovisioner.NewCAASOperatorProvisionerAPI(
    56  		s.resources, s.authorizer, s.st, s.st, s.storagePoolManager, s.registry)
    57  	c.Assert(err, jc.ErrorIsNil)
    58  	s.api = api
    59  }
    60  
    61  func (s *CAASProvisionerSuite) TestPermission(c *gc.C) {
    62  	s.authorizer = &apiservertesting.FakeAuthorizer{
    63  		Tag: names.NewMachineTag("0"),
    64  	}
    65  	_, err := caasoperatorprovisioner.NewCAASOperatorProvisionerAPI(
    66  		s.resources, s.authorizer, s.st, s.st, s.storagePoolManager, s.registry)
    67  	c.Assert(err, gc.ErrorMatches, "permission denied")
    68  }
    69  
    70  func (s *CAASProvisionerSuite) TestWatchApplications(c *gc.C) {
    71  	applicationNames := []string{"db2", "hadoop"}
    72  	s.st.applicationWatcher.changes <- applicationNames
    73  	result, err := s.api.WatchApplications()
    74  	c.Assert(err, jc.ErrorIsNil)
    75  	c.Assert(result.Error, gc.IsNil)
    76  	c.Assert(result.StringsWatcherId, gc.Equals, "1")
    77  	c.Assert(result.Changes, jc.DeepEquals, applicationNames)
    78  
    79  	resource := s.resources.Get("1")
    80  	c.Assert(resource, gc.NotNil)
    81  	c.Assert(resource, gc.Implements, new(state.StringsWatcher))
    82  }
    83  
    84  func (s *CAASProvisionerSuite) TestSetPasswords(c *gc.C) {
    85  	s.st.app = &mockApplication{
    86  		tag: names.NewApplicationTag("app"),
    87  	}
    88  
    89  	args := params.EntityPasswords{
    90  		Changes: []params.EntityPassword{
    91  			{Tag: "application-app", Password: "xxx-12345678901234567890"},
    92  			{Tag: "application-another", Password: "yyy-12345678901234567890"},
    93  			{Tag: "machine-0", Password: "zzz-12345678901234567890"},
    94  		},
    95  	}
    96  	results, err := s.api.SetPasswords(args)
    97  	c.Assert(err, jc.ErrorIsNil)
    98  	c.Assert(results, gc.DeepEquals, params.ErrorResults{
    99  		Results: []params.ErrorResult{
   100  			{nil},
   101  			{&params.Error{Message: "entity application-another not found", Code: "not found"}},
   102  			{&params.Error{Message: "permission denied", Code: "unauthorized access"}},
   103  		},
   104  	})
   105  	c.Assert(s.st.app.password, gc.Equals, "xxx-12345678901234567890")
   106  }
   107  
   108  func (s *CAASProvisionerSuite) TestLife(c *gc.C) {
   109  	s.st.app = &mockApplication{
   110  		tag: names.NewApplicationTag("app"),
   111  	}
   112  	results, err := s.api.Life(params.Entities{
   113  		Entities: []params.Entity{
   114  			{Tag: "application-app"},
   115  			{Tag: "machine-0"},
   116  		},
   117  	})
   118  	c.Assert(err, jc.ErrorIsNil)
   119  	c.Assert(results, jc.DeepEquals, params.LifeResults{
   120  		Results: []params.LifeResult{{
   121  			Life: life.Alive,
   122  		}, {
   123  			Error: &params.Error{
   124  				Code:    "unauthorized access",
   125  				Message: "permission denied",
   126  			},
   127  		}},
   128  	})
   129  }
   130  
   131  func (s *CAASProvisionerSuite) TestOperatorProvisioningInfoDefault(c *gc.C) {
   132  	s.st.app = &mockApplication{
   133  		charm: &mockCharm{meta: &charm.Meta{}},
   134  	}
   135  	result, err := s.api.OperatorProvisioningInfo(params.Entities{Entities: []params.Entity{{"application-gitlab"}}})
   136  	c.Assert(err, jc.ErrorIsNil)
   137  	c.Assert(result, jc.DeepEquals, params.OperatorProvisioningInfoResults{
   138  		Results: []params.OperatorProvisioningInfo{{
   139  			ImageDetails: params.DockerImageInfo{
   140  				RegistryPath: "docker.io/jujusolutions/jujud-operator:2.6-beta3.666",
   141  			},
   142  			BaseImageDetails: params.DockerImageInfo{
   143  				RegistryPath: "docker.io/jujusolutions/charm-base:ubuntu-20.04",
   144  			},
   145  			Version:      version.MustParse("2.6-beta3.666"),
   146  			APIAddresses: []string{"10.0.0.1:1"},
   147  			Tags: map[string]string{
   148  				"juju-model-uuid":      coretesting.ModelTag.Id(),
   149  				"juju-controller-uuid": coretesting.ControllerTag.Id()},
   150  			CharmStorage: &params.KubernetesFilesystemParams{
   151  				StorageName: "charm",
   152  				Size:        uint64(1024),
   153  				Provider:    "kubernetes",
   154  				Attributes: map[string]interface{}{
   155  					"storage-class": "k8s-storage",
   156  					"foo":           "bar",
   157  				},
   158  				Tags: map[string]string{
   159  					"juju-model-uuid":      coretesting.ModelTag.Id(),
   160  					"juju-controller-uuid": coretesting.ControllerTag.Id()},
   161  			},
   162  		}},
   163  	})
   164  }
   165  
   166  func (s *CAASProvisionerSuite) TestOperatorProvisioningInfo(c *gc.C) {
   167  	s.st.operatorRepo = "somerepo"
   168  	s.st.app = &mockApplication{
   169  		charm: &mockCharm{meta: &charm.Meta{}},
   170  	}
   171  	result, err := s.api.OperatorProvisioningInfo(params.Entities{Entities: []params.Entity{{"application-gitlab"}}})
   172  	c.Assert(err, jc.ErrorIsNil)
   173  	c.Assert(result, jc.DeepEquals, params.OperatorProvisioningInfoResults{
   174  		Results: []params.OperatorProvisioningInfo{{
   175  			ImageDetails: params.DockerImageInfo{
   176  				RegistryPath: s.st.operatorRepo + "/jujud-operator:" + "2.6-beta3.666",
   177  				Repository:   s.st.operatorRepo,
   178  			},
   179  			BaseImageDetails: params.DockerImageInfo{
   180  				RegistryPath: s.st.operatorRepo + "/charm-base:ubuntu-20.04",
   181  				Repository:   s.st.operatorRepo,
   182  			},
   183  			Version:      version.MustParse("2.6-beta3.666"),
   184  			APIAddresses: []string{"10.0.0.1:1"},
   185  			Tags: map[string]string{
   186  				"juju-model-uuid":      coretesting.ModelTag.Id(),
   187  				"juju-controller-uuid": coretesting.ControllerTag.Id()},
   188  			CharmStorage: &params.KubernetesFilesystemParams{
   189  				StorageName: "charm",
   190  				Size:        uint64(1024),
   191  				Provider:    "kubernetes",
   192  				Attributes: map[string]interface{}{
   193  					"storage-class": "k8s-storage",
   194  					"foo":           "bar",
   195  				},
   196  				Tags: map[string]string{
   197  					"juju-model-uuid":      coretesting.ModelTag.Id(),
   198  					"juju-controller-uuid": coretesting.ControllerTag.Id()},
   199  			},
   200  		}},
   201  	})
   202  }
   203  
   204  func (s *CAASProvisionerSuite) TestOperatorProvisioningInfoNoStorage(c *gc.C) {
   205  	s.st.operatorRepo = "somerepo"
   206  	minVers := version.MustParse("2.8.0")
   207  	s.st.app = &mockApplication{
   208  		charm: &mockCharm{meta: &charm.Meta{MinJujuVersion: minVers}},
   209  	}
   210  	result, err := s.api.OperatorProvisioningInfo(params.Entities{Entities: []params.Entity{{"application-gitlab"}}})
   211  	c.Assert(err, jc.ErrorIsNil)
   212  	c.Assert(result, jc.DeepEquals, params.OperatorProvisioningInfoResults{
   213  		Results: []params.OperatorProvisioningInfo{{
   214  			ImageDetails: params.DockerImageInfo{
   215  				RegistryPath: s.st.operatorRepo + "/jujud-operator:" + "2.6-beta3.666",
   216  				Repository:   s.st.operatorRepo,
   217  			},
   218  			BaseImageDetails: params.DockerImageInfo{
   219  				RegistryPath: s.st.operatorRepo + "/charm-base:ubuntu-20.04",
   220  				Repository:   s.st.operatorRepo,
   221  			},
   222  			Version:      version.MustParse("2.6-beta3.666"),
   223  			APIAddresses: []string{"10.0.0.1:1"},
   224  			Tags: map[string]string{
   225  				"juju-model-uuid":      coretesting.ModelTag.Id(),
   226  				"juju-controller-uuid": coretesting.ControllerTag.Id()},
   227  		}},
   228  	})
   229  }
   230  
   231  func (s *CAASProvisionerSuite) TestOperatorProvisioningInfoSidecarNoStorage(c *gc.C) {
   232  	s.st.operatorRepo = "somerepo"
   233  	s.st.app = &mockApplication{
   234  		charm: &mockCharm{
   235  			meta:     &charm.Meta{},
   236  			manifest: &charm.Manifest{Bases: []charm.Base{{}}}},
   237  	}
   238  	result, err := s.api.OperatorProvisioningInfo(params.Entities{Entities: []params.Entity{{"application-gitlab"}}})
   239  	c.Assert(err, jc.ErrorIsNil)
   240  	c.Assert(result, jc.DeepEquals, params.OperatorProvisioningInfoResults{
   241  		Results: []params.OperatorProvisioningInfo{{
   242  			ImageDetails: params.DockerImageInfo{
   243  				RegistryPath: s.st.operatorRepo + "/jujud-operator:" + "2.6-beta3.666",
   244  				Repository:   s.st.operatorRepo,
   245  			},
   246  			BaseImageDetails: params.DockerImageInfo{
   247  				RegistryPath: s.st.operatorRepo + "/charm-base:ubuntu-20.04",
   248  				Repository:   s.st.operatorRepo,
   249  			},
   250  			Version:      version.MustParse("2.6-beta3.666"),
   251  			APIAddresses: []string{"10.0.0.1:1"},
   252  			Tags: map[string]string{
   253  				"juju-model-uuid":      coretesting.ModelTag.Id(),
   254  				"juju-controller-uuid": coretesting.ControllerTag.Id()},
   255  		}},
   256  	})
   257  }
   258  
   259  func (s *CAASProvisionerSuite) TestOperatorProvisioningInfoNoStoragePool(c *gc.C) {
   260  	s.storagePoolManager.SetErrors(errors.NotFoundf("pool"))
   261  	s.st.operatorRepo = "somerepo"
   262  	minVers := version.MustParse("2.7.0")
   263  	s.st.app = &mockApplication{
   264  		charm: &mockCharm{meta: &charm.Meta{MinJujuVersion: minVers}},
   265  	}
   266  	result, err := s.api.OperatorProvisioningInfo(params.Entities{Entities: []params.Entity{{"application-gitlab"}}})
   267  	c.Assert(err, jc.ErrorIsNil)
   268  	c.Assert(result, jc.DeepEquals, params.OperatorProvisioningInfoResults{
   269  		Results: []params.OperatorProvisioningInfo{{
   270  			ImageDetails: params.DockerImageInfo{
   271  				RegistryPath: s.st.operatorRepo + "/jujud-operator:" + "2.6-beta3.666",
   272  				Repository:   s.st.operatorRepo,
   273  			},
   274  			BaseImageDetails: params.DockerImageInfo{
   275  				RegistryPath: s.st.operatorRepo + "/charm-base:ubuntu-20.04",
   276  				Repository:   s.st.operatorRepo,
   277  			},
   278  			Version:      version.MustParse("2.6-beta3.666"),
   279  			APIAddresses: []string{"10.0.0.1:1"},
   280  			Tags: map[string]string{
   281  				"juju-model-uuid":      coretesting.ModelTag.Id(),
   282  				"juju-controller-uuid": coretesting.ControllerTag.Id()},
   283  			CharmStorage: &params.KubernetesFilesystemParams{
   284  				StorageName: "charm",
   285  				Size:        uint64(1024),
   286  				Provider:    "kubernetes",
   287  				Attributes: map[string]interface{}{
   288  					"storage-class": "k8s-storage",
   289  				},
   290  				Tags: map[string]string{
   291  					"juju-model-uuid":      coretesting.ModelTag.Id(),
   292  					"juju-controller-uuid": coretesting.ControllerTag.Id()},
   293  			},
   294  		}},
   295  	})
   296  }
   297  
   298  func (s *CAASProvisionerSuite) TestAddresses(c *gc.C) {
   299  	_, err := s.api.APIAddresses()
   300  	c.Assert(err, jc.ErrorIsNil)
   301  	s.st.CheckCallNames(c, "APIHostPortsForAgents")
   302  }
   303  
   304  func (s *CAASProvisionerSuite) TestIssueOperatorCertificate(c *gc.C) {
   305  	res, err := s.api.IssueOperatorCertificate(params.Entities{
   306  		Entities: []params.Entity{{Tag: "application-appname"}},
   307  	})
   308  	c.Assert(err, jc.ErrorIsNil)
   309  	s.st.CheckCallNames(c, "StateServingInfo")
   310  	c.Assert(res.Results, gc.HasLen, 1)
   311  	certInfo := res.Results[0]
   312  	c.Assert(certInfo.Error, gc.IsNil)
   313  
   314  	certs, signers, err := pki.UnmarshalPemData(append([]byte(certInfo.Cert),
   315  		[]byte(certInfo.PrivateKey)...))
   316  	c.Assert(err, jc.ErrorIsNil)
   317  	c.Assert(len(signers), gc.Equals, 1)
   318  	c.Assert(len(certs), gc.Equals, 2)
   319  
   320  	roots := x509.NewCertPool()
   321  	ok := roots.AppendCertsFromPEM([]byte(certInfo.CACert))
   322  	c.Assert(ok, jc.IsTrue)
   323  	_, err = certs[0].Verify(x509.VerifyOptions{
   324  		DNSName: "appname",
   325  		Roots:   roots,
   326  	})
   327  	c.Assert(err, jc.ErrorIsNil)
   328  }