Skip to content

Commit

Permalink
Merge pull request juju#18541 from wallyworld/remove-k8s-operator-sto…
Browse files Browse the repository at this point in the history
…rage

juju#18541

We no longer support specifying operator storage for k8s charms since this was only required for Juju prior to 2.8 and only for older operator charms. This PR removes the model config attributes and associated processing when performing add-k8s etc. It's essentially all code deletion except for the items below.

When migrating an older model, we also want to not import the deprecated model config attribute. There's also other attributes which fall into this category, eg secret-backend, logging-output etc. So the import service for model config has extra logic added - it loads up the model defaults and extracts the attribute names. Any incoming attributes not in this list are excluded from the import.

The other extra change is to ensure workload storage is validated against the k8s cluster at deploy time. This was being done for operator storage but not workload storage, which was an omission.

## QA steps

We need to check that add-k8s correctly probes for storage and sets cloud config. This can be done on microk8s

Without explicitly specifying workload storage
```
microk8s config > ~/.kube/config
juju add-k8s mtest
k8s substrate "microk8s/localhost" added as cloud "mtest".
You can now bootstrap to this cloud by running 'juju bootstrap mtest'.

juju show-cloud mtest
...
config:
 workload-storage: microk8s-hostpath
...
```

Specifying a non default storage class - create a storage class called "test" on the cluster.
```
juju add-k8s mtest2 --storage test
k8s substrate "microk8s/localhost" added as cloud "mtest2" with storage provisioned
by the existing "test" storage class.
You can now bootstrap to this cloud by running 'juju bootstrap mtest2'

juju show-cloud mtest2
...
config:
 workload-storage: test
...
```

deploy a charm
```
juju deploy mysql-k8s --channel=8.0
```
or
```
juju deploy mysql-k8s --channel=8.0 --storage database=test
```

migrate a 3.6 model
```
juju40 bootstrap microk8s c40
juju bootstrap microk8s c36
juju add-model foo
juju model-config workload-storage=foo
juju migrate foo c40
juju switch c40:foo
juju model-config
```

## Documentation changes

@tmihoc we'll need to update the doc to remove any references to "operator-storage" config on k8s clouds.

## Links

**Jira card:** [JUJU-6352](https://warthogs.atlassian.net/browse/JUJU-6352)



[JUJU-6352]: https://warthogs.atlassian.net/browse/JUJU-6352?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
  • Loading branch information
jujubot authored Dec 18, 2024
2 parents 17a4ce1 + 5ee2a69 commit f28642c
Show file tree
Hide file tree
Showing 27 changed files with 104 additions and 507 deletions.
33 changes: 7 additions & 26 deletions apiserver/facades/client/application/application.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import (
apiservererrors "github.com/juju/juju/apiserver/errors"
"github.com/juju/juju/apiserver/facade"
"github.com/juju/juju/caas"
k8s "github.com/juju/juju/caas/kubernetes/provider"
k8sconstants "github.com/juju/juju/caas/kubernetes/provider/constants"
corebase "github.com/juju/juju/core/base"
corecharm "github.com/juju/juju/core/charm"
Expand Down Expand Up @@ -102,7 +101,6 @@ type APIBase struct {

type CaasBrokerInterface interface {
ValidateStorageClass(ctx context.Context, config map[string]interface{}) error
Version() (*version.Number, error)
}

func newFacadeBase(stdCtx context.Context, ctx facade.ModelContext) (*APIBase, error) {
Expand Down Expand Up @@ -454,23 +452,17 @@ func (c caasDeployParams) precheck(
return fmt.Errorf("getting model config: %w", err)
}

// For older charms, operator-storage model config is mandatory.
if k8s.RequireOperatorStorage(c.charm) {
storageClassName, _ := cfg.AllAttrs()[k8sconstants.OperatorStorageKey].(string)
if storageClassName == "" {
return errors.New(
"deploying this Kubernetes application requires a suitable storage class.\n" +
"None have been configured. Set the operator-storage model config to " +
"specify which storage class should be used to allocate operator storage.\n" +
"See https://discourse.charmhub.io/t/getting-started/152.",
)
workloadStorageClass, _ := cfg.AllAttrs()[k8sconstants.WorkloadStorageKey].(string)
for storageName, cons := range c.storage {
if cons.Pool == "" && workloadStorageClass == "" {
return errors.Errorf("storage pool for %q must be specified since there's no model default storage class", storageName)
}
sp, err := charmStorageParams(ctx, "", storageClassName, cfg, "", storageService, registry)
sp, err := charmStorageParams(ctx, "", workloadStorageClass, cfg, cons.Pool, storageService, registry)
if err != nil {
return errors.Annotatef(err, "getting operator storage params for %q", c.applicationName)
return errors.Annotatef(err, "getting workload storage params for %q", c.applicationName)
}
if sp.Provider != string(k8sconstants.StorageProviderType) {
poolName := cfg.AllAttrs()[k8sconstants.OperatorStorageKey]
poolName := cfg.AllAttrs()[k8sconstants.WorkloadStorageKey]
return errors.Errorf(
"the %q storage pool requires a provider type of %q, not %q", poolName, k8sconstants.StorageProviderType, sp.Provider)
}
Expand All @@ -479,17 +471,6 @@ func (c caasDeployParams) precheck(
}
}

workloadStorageClass, _ := cfg.AllAttrs()[k8sconstants.WorkloadStorageKey].(string)
for storageName, cons := range c.storage {
if cons.Pool == "" && workloadStorageClass == "" {
return errors.Errorf("storage pool for %q must be specified since there's no model default storage class", storageName)
}
_, err := charmStorageParams(ctx, "", workloadStorageClass, cfg, cons.Pool, storageService, registry)
if err != nil {
return errors.Annotatef(err, "getting workload storage params for %q", c.applicationName)
}
}

return nil
}

Expand Down
40 changes: 0 additions & 40 deletions apiserver/facades/client/application/legacy_mock_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -815,7 +815,6 @@ func (s *CAASApplicationProvisionerSuite) TestCharmStorageParamsPoolNotFound(c *

func (s *CAASApplicationProvisionerSuite) fakeModelConfig() (*envconfig.Config, error) {
attrs := coretesting.FakeConfig()
attrs["operator-storage"] = "k8s-storage"
attrs["agent-version"] = "2.6-beta3.666"
return envconfig.New(envconfig.UseDefaults, attrs)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ func (m *mockModel) UUID() string {

func (m *mockModel) ModelConfig(_ context.Context) (*config.Config, error) {
attrs := coretesting.FakeConfig()
attrs["operator-storage"] = "k8s-storage"
attrs["agent-version"] = "2.6-beta3"
return config.New(config.UseDefaults, attrs)
}
Expand Down
1 change: 0 additions & 1 deletion caas/kubernetes/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ type ClusterMetadataChecker interface {
// ClusterMetadata defines metadata about a cluster.
type ClusterMetadata struct {
WorkloadStorageClass *storagev1.StorageClass
OperatorStorageClass *storagev1.StorageClass
Cloud string
Regions set.Strings
}
Expand Down
2 changes: 0 additions & 2 deletions caas/kubernetes/provider/base_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,6 @@ func (s *BaseSuite) SetUpTest(c *gc.C) {
// init config for each test for easier changing config inside test.
cfg, err := config.New(config.UseDefaults, coretesting.FakeConfig().Merge(coretesting.Attrs{
config.NameKey: "test",
k8sconstants.OperatorStorageKey: "",
k8sconstants.WorkloadStorageKey: "",
}))
c.Assert(err, jc.ErrorIsNil)
Expand Down Expand Up @@ -499,7 +498,6 @@ func (s *fakeClientSuite) SetUpTest(c *gc.C) {

s.cfg, err = config.New(config.UseDefaults, coretesting.FakeConfig().Merge(coretesting.Attrs{
config.NameKey: "test",
k8sconstants.OperatorStorageKey: "",
k8sconstants.WorkloadStorageKey: "",
}))
c.Assert(err, jc.ErrorIsNil)
Expand Down
1 change: 0 additions & 1 deletion caas/kubernetes/provider/bootstrap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ func (s *bootstrapSuite) SetUpTest(c *gc.C) {

cfg, err := config.New(config.UseDefaults, coretesting.FakeConfig().Merge(coretesting.Attrs{
config.NameKey: "controller-1",
k8sconstants.OperatorStorageKey: "",
k8sconstants.WorkloadStorageKey: "",
}))
c.Assert(err, jc.ErrorIsNil)
Expand Down
11 changes: 1 addition & 10 deletions caas/kubernetes/provider/cloud.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,19 +84,10 @@ func UpdateKubeCloudWithStorage(k8sCloud cloud.Cloud, storageParams KubeCloudSto
}
}

// We at least expected operator storage to be available to successfully use
// this cloud.
if clusterMetadata.OperatorStorageClass == nil {
return cloud.Cloud{}, &environs.PreferredStorageNotFound{
Message: "no preferred operator storage found in Kubernetes cluster",
}
}

if k8sCloud.Config == nil {
k8sCloud.Config = make(map[string]interface{})
}

k8sCloud.Config[k8sconstants.OperatorStorageKey] = clusterMetadata.OperatorStorageClass.Name
k8sCloud.Config[k8sconstants.WorkloadStorageKey] = ""
if clusterMetadata.WorkloadStorageClass != nil {
k8sCloud.Config[k8sconstants.WorkloadStorageKey] = clusterMetadata.WorkloadStorageClass.Name
Expand Down Expand Up @@ -140,7 +131,7 @@ func (p kubernetesEnvironProvider) FinalizeCloud(ctx environs.FinalizeCloudConte
cld.AuthTypes = k8scloud.SupportedAuthTypes()

// if storage is already defined there is no need to query the cluster
if opStorage, ok := cld.Config[k8sconstants.OperatorStorageKey]; ok && opStorage != "" {
if opStorage, ok := cld.Config[k8sconstants.WorkloadStorageKey]; ok && opStorage != "" {
return cld, nil
}

Expand Down
37 changes: 3 additions & 34 deletions caas/kubernetes/provider/cloud_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import (

"github.com/juju/juju/caas"
k8s "github.com/juju/juju/caas/kubernetes"
k8scloud "github.com/juju/juju/caas/kubernetes/cloud"
"github.com/juju/juju/caas/kubernetes/provider"
k8sutils "github.com/juju/juju/caas/kubernetes/provider/utils"
jujucloud "github.com/juju/juju/cloud"
Expand All @@ -45,9 +44,9 @@ var defaultK8sCloud = jujucloud.Cloud{
var defaultClusterMetadata = &k8s.ClusterMetadata{
Cloud: k8s.K8sCloudMicrok8s,
Regions: set.NewStrings(k8s.Microk8sRegion),
OperatorStorageClass: &storagev1.StorageClass{
WorkloadStorageClass: &storagev1.StorageClass{
ObjectMeta: metav1.ObjectMeta{
Name: "operator-sc",
Name: "workload-sc",
},
},
}
Expand Down Expand Up @@ -104,37 +103,7 @@ func (s *cloudSuite) TestFinalizeCloudMicrok8s(c *gc.C) {
SkipTLSVerify: true,
Endpoint: "http://1.1.1.1:8080",
HostCloudRegion: fmt.Sprintf("%s/%s", k8s.K8sCloudMicrok8s, k8s.Microk8sRegion),
Config: map[string]interface{}{"operator-storage": "operator-sc", "workload-storage": ""},
Regions: []jujucloud.Region{{Name: k8s.Microk8sRegion, Endpoint: "http://1.1.1.1:8080"}},
})
}

func (s *cloudSuite) TestFinalizeCloudMicrok8sAlreadyStorage(c *gc.C) {
preparedCloud := jujucloud.Cloud{
Name: k8s.K8sCloudMicrok8s,
Type: jujucloud.CloudTypeKubernetes,
AuthTypes: []jujucloud.AuthType{jujucloud.UserPassAuthType},
CACertificates: []string{""},
Endpoint: "http://1.1.1.1:8080",
HostCloudRegion: fmt.Sprintf("%s/%s", k8s.K8sCloudMicrok8s, k8s.Microk8sRegion),
Config: map[string]interface{}{"operator-storage": "something-else", "workload-storage": ""},
Regions: []jujucloud.Region{{Name: k8s.Microk8sRegion, Endpoint: "http://1.1.1.1:8080"}},
}

p := s.getProvider()
cloudFinalizer := p.(environs.CloudFinalizer)

ctx := mockContext{Context: context.Background()}
cloud, err := cloudFinalizer.FinalizeCloud(&ctx, preparedCloud)
c.Assert(err, jc.ErrorIsNil)
c.Assert(cloud, jc.DeepEquals, jujucloud.Cloud{
Name: k8s.K8sCloudMicrok8s,
Type: jujucloud.CloudTypeKubernetes,
AuthTypes: k8scloud.SupportedAuthTypes(),
CACertificates: []string{""},
Endpoint: "http://1.1.1.1:8080",
HostCloudRegion: fmt.Sprintf("%s/%s", k8s.K8sCloudMicrok8s, k8s.Microk8sRegion),
Config: map[string]interface{}{"operator-storage": "something-else", "workload-storage": ""},
Config: map[string]interface{}{"workload-storage": "workload-sc"},
Regions: []jujucloud.Region{{Name: k8s.Microk8sRegion, Endpoint: "http://1.1.1.1:8080"}},
})
}
Expand Down
4 changes: 0 additions & 4 deletions caas/kubernetes/provider/constants/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,6 @@ const (
// WorkloadStorageKey is the model config attribute used to specify
// the storage class for provisioning workload storage.
WorkloadStorageKey = "workload-storage"

// OperatorStorageKey is the model config attribute used to specify
// the storage class for provisioning operator storage.
OperatorStorageKey = "operator-storage"
)

// QualifiedStorageClassName returns a qualified storage class name.
Expand Down
11 changes: 6 additions & 5 deletions caas/kubernetes/provider/k8s.go
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ Please bootstrap again and choose a different controller name.`, controllerName)
// The namespace will be set to controller-name in newcontrollerStack.

// do validation on storage class.
_, err = k.validateOperatorStorage(ctx)
_, err = k.validateControllerWorkloadStorage(ctx)
return errors.Trace(err)
}

Expand Down Expand Up @@ -417,10 +417,10 @@ func (k *kubernetesClient) EnsureImageRepoSecret(ctx context.Context, imageRepo
return errors.Trace(err)
}

func (k *kubernetesClient) validateOperatorStorage(ctx context.Context) (string, error) {
storageClass, _ := k.Config().AllAttrs()[constants.OperatorStorageKey].(string)
func (k *kubernetesClient) validateControllerWorkloadStorage(ctx context.Context) (string, error) {
storageClass, _ := k.Config().AllAttrs()[constants.WorkloadStorageKey].(string)
if storageClass == "" {
return "", errors.NewNotValid(nil, "config without operator-storage value not valid.\nRun juju add-k8s to reimport your k8s cluster.")
return "", errors.NewNotValid(nil, "config without workload-storage value not valid.\nRun juju add-k8s to reimport your k8s cluster.")
}
_, err := k.getStorageClass(ctx, storageClass)
return storageClass, errors.Trace(err)
Expand All @@ -437,7 +437,8 @@ func (k *kubernetesClient) Bootstrap(
return nil, errors.NotSupportedf("set base for bootstrapping to kubernetes")
}

storageClass, err := k.validateOperatorStorage(ctx)
// Validate workload storage is available to the controller.
storageClass, err := k.validateControllerWorkloadStorage(ctx)
if err != nil {
return nil, errors.Trace(err)
}
Expand Down
19 changes: 8 additions & 11 deletions caas/kubernetes/provider/k8s_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ func (s *K8sBrokerSuite) TestSetConfig(c *gc.C) {
c.Assert(err, jc.ErrorIsNil)
}

func (s *K8sBrokerSuite) TestBootstrapNoOperatorStorage(c *gc.C) {
func (s *K8sBrokerSuite) TestBootstrapNoWorkloadStorage(c *gc.C) {
ctrl := s.setupController(c)
defer ctrl.Finish()

Expand All @@ -260,15 +260,15 @@ func (s *K8sBrokerSuite) TestBootstrapNoOperatorStorage(c *gc.C) {
_, err := s.broker.Bootstrap(ctx, callCtx, bootstrapParams)
c.Assert(err, gc.NotNil)
msg := strings.Replace(err.Error(), "\n", "", -1)
c.Assert(msg, gc.Matches, "config without operator-storage value not valid.*")
c.Assert(msg, gc.Matches, "config without workload-storage value not valid.*")
}

func (s *K8sBrokerSuite) TestBootstrap(c *gc.C) {
ctrl := s.setupController(c)
defer ctrl.Finish()

// Ensure the broker is configured with operator storage.
s.setupOperatorStorageConfig(c)
// Ensure the broker is configured with workload storage.
s.setupWorkloadStorageConfig(c)

ctx := envtesting.BootstrapContext(context.Background(), c)
callCtx := envcontext.WithoutCredentialInvalidator(ctx)
Expand All @@ -284,10 +284,7 @@ func (s *K8sBrokerSuite) TestBootstrap(c *gc.C) {
},
}
gomock.InOrder(
// Check the operator storage exists.
s.mockStorageClass.EXPECT().Get(gomock.Any(), "test-some-storage", v1.GetOptions{}).
Return(nil, s.k8sNotFoundError()),
s.mockStorageClass.EXPECT().Get(gomock.Any(), "some-storage", v1.GetOptions{}).
Return(sc, nil),
)
result, err := s.broker.Bootstrap(ctx, callCtx, bootstrapParams)
Expand All @@ -300,10 +297,10 @@ func (s *K8sBrokerSuite) TestBootstrap(c *gc.C) {
c.Assert(err, jc.ErrorIs, errors.NotSupported)
}

func (s *K8sBrokerSuite) setupOperatorStorageConfig(c *gc.C) {
func (s *K8sBrokerSuite) setupWorkloadStorageConfig(c *gc.C) {
cfg := s.broker.Config()
var err error
cfg, err = cfg.Apply(map[string]interface{}{"operator-storage": "some-storage"})
cfg, err = cfg.Apply(map[string]interface{}{"workload-storage": "some-storage"})
c.Assert(err, jc.ErrorIsNil)
err = s.broker.SetConfig(context.Background(), cfg)
c.Assert(err, jc.ErrorIsNil)
Expand All @@ -313,8 +310,8 @@ func (s *K8sBrokerSuite) TestPrepareForBootstrap(c *gc.C) {
ctrl := s.setupController(c)
defer ctrl.Finish()

// Ensure the broker is configured with operator storage.
s.setupOperatorStorageConfig(c)
// Ensure the broker is configured with workload storage.
s.setupWorkloadStorageConfig(c)

sc := &storagev1.StorageClass{
ObjectMeta: v1.ObjectMeta{
Expand Down
Loading

0 comments on commit f28642c

Please sign in to comment.