Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions lxc/completion.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

"github.com/spf13/cobra"

"github.com/canonical/lxd/lxd/instance/instancetype"
"github.com/canonical/lxd/shared"
"github.com/canonical/lxd/shared/api"
)
Expand Down Expand Up @@ -357,8 +358,10 @@ func (g *cmdGlobal) cmpInstanceSetKeys(instanceName string) ([]string, cobra.She
return nil, cobra.ShellCompDirectiveError
}

// Fetch all config keys that can be set by a user.
allInstanceConfigKeys, _ := g.cmpInstanceAllKeys(instanceName)
instanceType := instance.Type

// Fetch all config keys that can be set by a user based on instance type.
allInstanceConfigKeys, _ := g.cmpInstanceKeys(instanceName)

// Convert slice to map[string]struct{} for O(1) lookups.
keySet := make(map[string]struct{}, len(allInstanceConfigKeys))
Expand All @@ -374,7 +377,11 @@ func (g *cmdGlobal) cmpInstanceSetKeys(instanceName string) ([]string, cobra.She
// We only want to return the intersection between allInstanceConfigKeys and configKeys to avoid returning the full instance config.
_, exists := keySet[configKey]
if exists {
configKeys = append(configKeys, configKey)
if shared.StringHasPrefix(configKey, instancetype.ConfigKeyPrefixesAny...) {
configKeys = append(configKeys, configKey)
} else if instanceType == string(api.InstanceTypeContainer) && shared.StringHasPrefix(configKey, instancetype.ConfigKeyPrefixesContainer...) {
configKeys = append(configKeys, configKey)
}
}
}

Expand Down
38 changes: 34 additions & 4 deletions lxd/instance/instance_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,40 @@ func ValidConfig(sysOS *sys.OS, config map[string]string, expanded bool, instanc
}

func validConfigKey(os *sys.OS, key string, value string, instanceType instancetype.Type) error {
// Check if the key is a valid prefix and whether or not it requires a subkey.
knownPrefixes := append(instancetype.ConfigKeyPrefixesAny, instancetype.ConfigKeyPrefixesContainer...)
if strings.HasSuffix(key, ".") {
// Disallow keys with container-specific prefixes such as "linux.sysctl." and "limits.kernel." for VMs.
if instanceType == instancetype.VM && shared.StringHasPrefix(key, instancetype.ConfigKeyPrefixesContainer...) {
return fmt.Errorf("%q isn't supported for %q", key, instanceType)
}

if !(key == instancetype.ConfigVolatilePrefix || shared.ValueInSlice(key, knownPrefixes)) {
// Not a known prefix.
return fmt.Errorf("Unknown configuration key: %q", key)
}

return fmt.Errorf("%q requires a subkey", key)
}

// Validate the configuration key against instance type for containers and VMs.
// Ignore configuration keys with known prefixes since usage has already been validated, and ConfigKeyChecker validates keys syntactically.
if instanceType != instancetype.Any && !shared.StringHasPrefix(key, knownPrefixes...) && !strings.HasPrefix(key, instancetype.ConfigVolatilePrefix) {
// Ensure key is present in instance config key map based on type.
exists := false
switch instanceType {
case instancetype.VM:
_, exists = instancetype.InstanceConfigKeysVM[key]
case instancetype.Container:
_, exists = instancetype.InstanceConfigKeysContainer[key]
}

_, existsAny := instancetype.InstanceConfigKeysAny[key]
if !(exists || existsAny) {
return fmt.Errorf("%q isn't supported for %q", key, instanceType)
}
}

f, err := instancetype.ConfigKeyChecker(key, instanceType)
if err != nil {
return err
Expand All @@ -137,10 +171,6 @@ func validConfigKey(os *sys.OS, key string, value string, instanceType instancet
return err
}

if strings.HasPrefix(key, "limits.kernel.") && instanceType == instancetype.VM {
return fmt.Errorf("%s isn't supported for VMs", key)
}

if key == "raw.lxc" {
return lxcValidConfig(value)
}
Expand Down
27 changes: 10 additions & 17 deletions lxd/instance/instancetype/instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ const (
// ConfigVolatilePrefix indicates the prefix used for volatile config keys.
const ConfigVolatilePrefix = "volatile."

// ConfigKeyPrefixesAny indicates valid prefixes for configuration options.
var ConfigKeyPrefixesAny = []string{"environment.", "user.", "image."}

// ConfigKeyPrefixesContainer indicates valid prefixes for container configuration options.
var ConfigKeyPrefixesContainer = []string{"linux.sysctl.", "limits.kernel."}

// ValidName validates an instance name. There are different validation rules for instance snapshot names
// so it takes an argument indicating whether the name is to be used for a snapshot or not.
func ValidName(instanceName string, isSnapshot bool) error {
Expand Down Expand Up @@ -1274,29 +1280,16 @@ func ConfigKeyChecker(key string, instanceType Type) (func(value string) error,
}
}

if strings.HasPrefix(key, "environment.") {
return validate.IsAny, nil
}

if strings.HasPrefix(key, "user.") {
return validate.IsAny, nil
}

if strings.HasPrefix(key, "image.") {
return validate.IsAny, nil
}

if strings.HasPrefix(key, "limits.kernel.") &&
(len(key) > len("limits.kernel.")) {
if (instanceType == Any || instanceType == Container) && strings.HasPrefix(key, "linux.sysctl.") {
return validate.IsAny, nil
}

if (instanceType == Any || instanceType == Container) &&
strings.HasPrefix(key, "linux.sysctl.") {
knownPrefixes := append(ConfigKeyPrefixesAny, ConfigKeyPrefixesContainer...)
if shared.StringHasPrefix(key, knownPrefixes...) {
return validate.IsAny, nil
}

return nil, fmt.Errorf("Unknown configuration key: %s", key)
return nil, fmt.Errorf("Unknown configuration key: %q", key)
}

// InstanceIncludeWhenCopying is used to decide whether to include a config item or not when copying an instance.
Expand Down
Loading