Skip to content

Commit

Permalink
Replicate k8s checking logic into storage package.
Browse files Browse the repository at this point in the history
  • Loading branch information
Juan Tirado committed Jun 7, 2022
1 parent 113892a commit 38f2b69
Show file tree
Hide file tree
Showing 17 changed files with 165 additions and 58 deletions.
6 changes: 1 addition & 5 deletions caas/kubernetes/provider/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,7 @@ var _ jujustorage.Provider = (*storageProvider)(nil)

//ValidateStorageProvider returns an error if the storage type and config is not valid
// for a Kubernetes deployment.
func (g *storageProvider) ValidateStorageProvider(isCaas bool, attributes map[string]any) error {

if !isCaas {
return errors.NotValidf("storage provider type %q", constants.StorageProviderType)
}
func (g *storageProvider) ValidateForK8s(attributes map[string]any) error {

if attributes == nil {
return nil
Expand Down
24 changes: 8 additions & 16 deletions caas/kubernetes/provider/storage_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import (
"github.com/juju/juju/caas/kubernetes/provider/constants"
"github.com/juju/juju/environs/context"
"github.com/juju/juju/storage"
storageprovider "github.com/juju/juju/storage/provider"
)

var _ = gc.Suite(&storageSuite{})
Expand Down Expand Up @@ -180,25 +179,18 @@ func (s *storageSuite) TestValidateStorageProvider(c *gc.C) {
prov := s.k8sProvider(c, ctrl)

for _, t := range []struct {
providerType storage.ProviderType
attrs map[string]interface{}
err string
attrs map[string]interface{}
err string
}{
{
providerType: storageprovider.RootfsProviderType,
}, {
providerType: storageprovider.TmpfsProviderType,
}, {
providerType: storageprovider.LoopProviderType,
err: `storage provider type "loop" not valid`,
}, {
providerType: storageprovider.TmpfsProviderType,
attrs: map[string]interface{}{"storage-medium": "foo"},
err: `storage medium "foo" not valid`,
attrs: map[string]interface{}{"storage-medium": "foo"},
err: `storage medium "foo" not valid`,
},
{
attrs: nil,
},
} {
err := prov.ValidateStorageProvider(t.providerType, t.attrs)
// err := provider.ValidateStorageProvider(t.providerType, t.attrs)
err := prov.ValidateForK8s(t.attrs)
if t.err == "" {
c.Check(err, jc.ErrorIsNil)
} else {
Expand Down
2 changes: 1 addition & 1 deletion provider/azure/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ func newAzureStorageConfig(attrs map[string]interface{}) (*azureStorageConfig, e
return azureStorageConfig, nil
}

func (e *azureStorageProvider) ValidateStorageProvider(bool, map[string]any) error {
func (e *azureStorageProvider) ValidateForK8s(map[string]any) error {
// no validation required
return nil
}
Expand Down
2 changes: 1 addition & 1 deletion provider/ec2/ebs.go
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ func newEbsConfig(attrs map[string]interface{}) (*ebsConfig, error) {
return ebsConfig, nil
}

func (e *ebsProvider) ValidateStorageProvider(bool, map[string]any) error {
func (e *ebsProvider) ValidateForK8s(map[string]any) error {
// no validation required
return nil
}
Expand Down
2 changes: 1 addition & 1 deletion provider/gce/disks.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ type storageProvider struct {

var _ storage.Provider = (*storageProvider)(nil)

func (g *storageProvider) ValidateStorageProvider(bool, map[string]any) error {
func (g *storageProvider) ValidateForK8s(map[string]any) error {
// no validation required
return nil
}
Expand Down
5 changes: 2 additions & 3 deletions provider/lxd/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,9 +130,8 @@ func newLXDStorageConfig(attrs map[string]interface{}) (*lxdStorageConfig, error
return lxdStorageConfig, nil
}

func (e *lxdStorageProvider) ValidateStorageProvider(bool, map[string]any) error {
// no validation required
return nil
func (e *lxdStorageProvider) ValidateForK8s(map[string]any) error {
return errors.NotValidf("storage provider type %q", lxdStorageProviderType)
}

// ValidateConfig is part of the Provider interface.
Expand Down
2 changes: 1 addition & 1 deletion provider/maas/volumes.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ func newStorageConfig(attrs map[string]interface{}) (*storageConfig, error) {
return &storageConfig{tags: tags}, nil
}

func (maasStorageProvider) ValidateStorageProvider(bool, map[string]any) error {
func (maasStorageProvider) ValidateForK8s(map[string]any) error {
// no validation required
return nil
}
Expand Down
5 changes: 2 additions & 3 deletions provider/oci/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,8 @@ func (s *storageProvider) DefaultPools() []*storage.Config {
return []*storage.Config{pool}
}

func (s *storageProvider) ValidateStorageProvider(bool, map[string]any) error {
// no validation required
return nil
func (s *storageProvider) ValidateForK8s(map[string]any) error {
return errors.NotValidf("storage provider type %q", ociStorageProviderType)
}

func (s *storageProvider) ValidateConfig(cfg *storage.Config) error {
Expand Down
5 changes: 2 additions & 3 deletions provider/openstack/cinder.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,9 +188,8 @@ func (s *cinderProvider) Scope() storage.Scope {
return storage.ScopeEnviron
}

func (p *cinderProvider) ValidateStorageProvider(bool, map[string]any) error {
// no validation required
return nil
func (p *cinderProvider) ValidateForK8s(map[string]any) error {
return errors.NotValidf("storage provider type %q", CinderProviderType)
}

// ValidateConfig implements storage.Provider.
Expand Down
8 changes: 5 additions & 3 deletions state/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -1875,9 +1875,11 @@ func validateStoragePool(
*machineId = ""
}
}
isCaas := sb.modelType == ModelTypeCAAS
if err := aProvider.ValidateStorageProvider(isCaas, poolConfig); err != nil {
return errors.Annotatef(err, "invalid storage config")
//
if sb.modelType == ModelTypeCAAS {
if err := aProvider.ValidateForK8s(poolConfig); err != nil {
return errors.Annotatef(err, "invalid storage config")
}
}

return nil
Expand Down
21 changes: 21 additions & 0 deletions storage/constants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright 2022 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package storage

const (
// K8sStorageMediumConst is the constant key
K8sStorageMediumConst = "storage-medium"
// K8sStorageMediumMemory is the value use tmpfs in K8s
K8sStorageMediumMemory = "Memory"
// K8sStorageMediumHugePages is a K8s storage for volumes
K8sStorageMediumHugePages = "HugePages"
)

const (
// StorageClass is the name of a storage class resource.
K8sStorageClass = "storage-class"
K8sStorageProvisioner = "storage-provisioner"
K8sStorageMedium = "storage-medium"
K8sStorageMode = "storage-mode"
)
6 changes: 3 additions & 3 deletions storage/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,9 @@ type Provider interface {
// returning an error if it is invalid.
ValidateConfig(*Config) error

// ValidateStorageProvider validates if for a CaaS model the
// current provider is valid with the given attributes.
ValidateStorageProvider(bool, map[string]any) error
// ValidateForkK8s validates if a storage provider can be set for
// a given K8s configuration.
ValidateForK8s(map[string]any) error
}

// VolumeSource provides an interface for creating, destroying, describing,
Expand Down
10 changes: 7 additions & 3 deletions storage/provider/dummy/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ type StorageProvider struct {
// otherwise ValidateConfig returns nil.
ValidateConfigFunc func(*storage.Config) error

// ValidateForK8sFunc will be called by ValidateForK8s, if non-nil;
// otherwise ValidateForK8s returns nil.
ValidateForK8sFunc func(map[string]any) error

// SupportsFunc will be called by Supports, if non-nil; otherwise,
// Supports returns true.
SupportsFunc func(kind storage.StorageKind) bool
Expand Down Expand Up @@ -76,9 +80,9 @@ func (p *StorageProvider) ValidateConfig(providerConfig *storage.Config) error {
return nil
}

func (p *StorageProvider) ValidateStorageProvider(bool, map[string]any) error {
// no validation required
return nil
func (p *StorageProvider) ValidateForK8s(attributes map[string]any) error {
p.MethodCall(p, "ValidateForK8s", attributes)
return p.ValidateForK8sFunc(attributes)
}

// Supports is defined on storage.Provider.
Expand Down
98 changes: 98 additions & 0 deletions storage/provider/k8s_validation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// Copyright 2022 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package provider

import (
"fmt"

"github.com/juju/errors"
"github.com/juju/juju/storage"
"github.com/juju/schema"
)

// checkK8sConfig checks that the attributes in a configuration
// are valid for a K8s deployment.
func checkK8sConfig(attributes map[string]any) error {
if mediumValue, ok := attributes[storage.K8sStorageMediumConst]; ok {
medium := fmt.Sprintf("%v", mediumValue)
if medium != storage.K8sStorageMediumMemory && medium != storage.K8sStorageMediumHugePages {
return errors.NotValidf("storage medium %q", mediumValue)
}
}

if err := validateStorageAttributes(attributes); err != nil {
return errors.Trace(err)
}

return nil
}

func validateStorageAttributes(attributes map[string]any) error {
if err := validateStorageConfig(attributes); err != nil {
return errors.Trace(err)
}
if err := validateStorageMode(attributes); err != nil {
return errors.Trace(err)
}
return nil
}

var storageConfigFields = schema.Fields{
storage.K8sStorageClass: schema.String(),
storage.K8sStorageProvisioner: schema.String(),
}

var storageConfigChecker = schema.FieldMap(
storageConfigFields,
schema.Defaults{
storage.K8sStorageClass: schema.Omit,
storage.K8sStorageProvisioner: schema.Omit,
},
)

// validateStorageConfig returns issues in the configuration if any.
func validateStorageConfig(attrs map[string]any) error {
out, err := storageConfigChecker.Coerce(attrs, nil)
if err != nil {
return errors.Annotate(err, "validating storage config")
}
coerced := out.(map[string]any)

if coerced[storage.K8sStorageProvisioner] != "" && coerced[storage.K8sStorageClass] == "" {
return errors.New("storage-class must be specified if storage-provisioner is specified")
}

return nil
}

var storageModeFields = schema.Fields{
storage.K8sStorageMode: schema.String(),
}

var storageModeChecker = schema.FieldMap(
storageModeFields,
schema.Defaults{
storage.K8sStorageMode: "ReadWriteOnce",
},
)

// validateStorageMode returns an error if the K8s persistent
// volume is not configured correctly.
func validateStorageMode(attrs map[string]any) error {
out, err := storageModeChecker.Coerce(attrs, nil)
if err != nil {
return errors.Annotate(err, "validating storage mode")
}
coerced := out.(map[string]any)
mode := coerced[storage.K8sStorageMode]
switch coerced[storage.K8sStorageMode] {
case "ReadOnlyMany", "ROX":
case "ReadWriteMany", "RWX":
case "ReadWriteOnce", "RWO":
default:
return errors.NotSupportedf("storage mode %q", mode)
}

return nil
}
8 changes: 2 additions & 6 deletions storage/provider/loop.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,8 @@ type loopProvider struct {

var _ storage.Provider = (*loopProvider)(nil)

func (*loopProvider) ValidateStorageProvider(isCaas bool, _ map[string]any) error {
// loop does not work with CaaS
if isCaas {
return errors.NotValidf("storage provider type %q", LoopProviderType)
}
return nil
func (*loopProvider) ValidateForK8s(map[string]any) error {
return errors.NotValidf("storage provider type %q", LoopProviderType)
}

// ValidateConfig is defined on the Provider interface.
Expand Down
10 changes: 5 additions & 5 deletions storage/provider/rootfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ var (
_ storage.Provider = (*rootfsProvider)(nil)
)

func (p *rootfsProvider) ValidateStorageProvider(isCaas bool, config map[string]any) error {
if isCaas {
return errors.NotValidf("storage provider type %q", RootfsProviderType)
func (p *rootfsProvider) ValidateForK8s(attributes map[string]any) error {
if attributes == nil {
return nil
}

return nil
// check the configuration
return checkK8sConfig(attributes)
}

// ValidateConfig is defined on the Provider interface.
Expand Down
9 changes: 5 additions & 4 deletions storage/provider/tmpfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,13 @@ var (
_ storage.Provider = (*tmpfsProvider)(nil)
)

func (p *tmpfsProvider) ValidateStorageProvider(isCaas bool, config map[string]any) error {
if !isCaas {
return errors.NotValidf("storage provider type %q", TmpfsProviderType)
func (p *tmpfsProvider) ValidateForK8s(attributes map[string]any) error {
if attributes == nil {
return nil
}

return nil
// check the configuration
return checkK8sConfig(attributes)
}

// ValidateConfig is defined on the Provider interface.
Expand Down

0 comments on commit 38f2b69

Please sign in to comment.