Skip to content
Draft
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
1 change: 1 addition & 0 deletions cns/azure-cns-windows.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ data:
},
"ChannelMode": "CRD",
"EnableBoltStateStore": false,
"EnablePersistentStateDebug": false,
"InitializeFromCNI": true,
"StateStoreBackend": "json",
"StateStoreMode": "normal"
Expand Down
1 change: 1 addition & 0 deletions cns/azure-cns.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ data:
},
"ChannelMode": "CRD",
"EnableBoltStateStore": false,
"EnablePersistentStateDebug": false,
"InitializeFromCNI": true,
"StateStoreBackend": "json",
"StateStoreMode": "normal",
Expand Down
1 change: 1 addition & 0 deletions cns/configuration/cns_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
},
"ChannelMode": "Direct",
"EnableBoltStateStore": false,
"EnablePersistentStateDebug": false,
"InitializeFromCNI": false,
"StateStoreBackend": "json",
"StateStoreMode": "normal",
Expand Down
38 changes: 32 additions & 6 deletions cns/configuration/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ type CNSConfig struct {
EnableIPAMv2 bool
EnableK8sDevicePlugin bool
EnableLoggerV2 bool
EnablePersistentStateDebug bool
EnablePprof bool
EnableStateMigration bool
EnableSubnetScarcity bool
Expand Down Expand Up @@ -112,13 +113,38 @@ func (cnsconfig CNSConfig) ValidateStateStore() error {
return fmt.Errorf("%w: mode %q", ErrInvalidStateStoreConfig, mode)
}

boltEnabled := cnsconfig.EnableBoltStateStore
boltSelected := backend == StateStoreBackendBolt
rollbackSelected := mode == StateStoreModeRollbackToJSON
if boltEnabled || boltSelected || rollbackSelected {
return ErrStateStoreFeatureUnavailable
if !cnsconfig.EnableBoltStateStore {
if backend != StateStoreBackendJSON || mode != StateStoreModeNormal || cnsconfig.EnablePersistentStateDebug {
return fmt.Errorf("%w: Bolt state store master flag is disabled", ErrInvalidStateStoreConfig)
}
return nil
}

switch {
case backend == StateStoreBackendBolt && mode == StateStoreModeNormal:
if !cnsconfig.ManageEndpointState {
return fmt.Errorf("%w: Bolt state store requires CNS-managed endpoint state", ErrInvalidStateStoreConfig)
}
if cnsconfig.EnableStateMigration && !cnsconfig.InitializeFromCNI {
return fmt.Errorf("%w: Bolt CNI ownership import requires CNI initialization", ErrInvalidStateStoreConfig)
}
return nil
case backend == StateStoreBackendJSON && mode == StateStoreModeRollbackToJSON:
if !cnsconfig.ManageEndpointState {
return fmt.Errorf("%w: Bolt rollback requires CNS-managed endpoint state", ErrInvalidStateStoreConfig)
}
if cnsconfig.EnablePersistentStateDebug {
return fmt.Errorf("%w: persistent state debug requires normal Bolt mode", ErrInvalidStateStoreConfig)
}
return nil
case backend == StateStoreBackendJSON && mode == StateStoreModeNormal:
if cnsconfig.EnablePersistentStateDebug {
return fmt.Errorf("%w: persistent state debug requires normal Bolt mode", ErrInvalidStateStoreConfig)
}
return nil
default:
return fmt.Errorf("%w: backend %q does not support mode %q", ErrInvalidStateStoreConfig, backend, mode)
}
return nil
}

type TelemetrySettings struct {
Expand Down
259 changes: 122 additions & 137 deletions cns/configuration/configuration_test.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,60 @@
package configuration

import (
"fmt"
"os"
"path/filepath"
"strings"
"testing"

"github.com/Azure/azure-container-networking/common"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestPersistentStateManifestDefaultsRemainDark(t *testing.T) {
repositoryRoot := filepath.Clean(filepath.Join("..", ".."))
root, err := os.OpenRoot(repositoryRoot)
require.NoError(t, err)
t.Cleanup(func() { require.NoError(t, root.Close()) })
required := []string{
`"EnableBoltStateStore": false`,
`"EnablePersistentStateDebug": false`,
`"StateStoreBackend": "json"`,
`"StateStoreMode": "normal"`,
}
found := 0
err = filepath.WalkDir(repositoryRoot, func(path string, entry os.DirEntry, walkErr error) error {
if walkErr != nil {
return fmt.Errorf("walking persistent state manifests: %w", walkErr)
}
if entry.IsDir() || (filepath.Ext(path) != ".json" && filepath.Ext(path) != ".yaml" && filepath.Ext(path) != ".yml") {
return nil
}
relativePath, relativeErr := filepath.Rel(repositoryRoot, path)
if relativeErr != nil {
return fmt.Errorf("resolving persistent state manifest path: %w", relativeErr)
}
contents, readErr := root.ReadFile(relativePath)
if readErr != nil {
return fmt.Errorf("reading persistent state manifest %q: %w", relativePath, readErr)
}
text := string(contents)
if !strings.Contains(text, `"EnableBoltStateStore"`) {
return nil
}
found++
for _, value := range required {
assert.Contains(t, text, value, "%s must explicitly preserve dark persistent-state defaults", path)
}
assert.NotContains(t, text, `"EnableBoltStateStore": true`, path)
assert.NotContains(t, text, `"StateStoreBackend": "bolt"`, path)
return nil
})
require.NoError(t, err)
require.Equal(t, 17, found)
}

func TestGetConfigFilePath(t *testing.T) {
execpath, _ := common.GetExecutableDirectory()

Expand Down Expand Up @@ -317,142 +362,83 @@ func TestSetCNSConfigDefaults(t *testing.T) {
}

func TestCNSConfigValidateStateStore(t *testing.T) {
tests := []struct {
name string
enableBoltStateStore bool
enableStateMigration bool
backend StateStoreBackend
mode StateStoreMode
wantErr error
}{
{
name: "json normal",
backend: StateStoreBackendJSON,
mode: StateStoreModeNormal,
},
{
name: "json rollback",
backend: StateStoreBackendJSON,
mode: StateStoreModeRollbackToJSON,
wantErr: ErrStateStoreFeatureUnavailable,
},
{
name: "bolt normal",
backend: StateStoreBackendBolt,
mode: StateStoreModeNormal,
wantErr: ErrStateStoreFeatureUnavailable,
},
{
name: "bolt rollback",
backend: StateStoreBackendBolt,
mode: StateStoreModeRollbackToJSON,
wantErr: ErrStateStoreFeatureUnavailable,
},
{
name: "enabled json normal",
enableBoltStateStore: true,
backend: StateStoreBackendJSON,
mode: StateStoreModeNormal,
wantErr: ErrStateStoreFeatureUnavailable,
},
{
name: "enabled json rollback",
enableBoltStateStore: true,
backend: StateStoreBackendJSON,
mode: StateStoreModeRollbackToJSON,
wantErr: ErrStateStoreFeatureUnavailable,
},
{
name: "enabled bolt normal",
enableBoltStateStore: true,
backend: StateStoreBackendBolt,
mode: StateStoreModeNormal,
wantErr: ErrStateStoreFeatureUnavailable,
},
{
name: "enabled bolt rollback",
enableBoltStateStore: true,
backend: StateStoreBackendBolt,
mode: StateStoreModeRollbackToJSON,
wantErr: ErrStateStoreFeatureUnavailable,
},
{
name: "migration json normal",
enableStateMigration: true,
backend: StateStoreBackendJSON,
mode: StateStoreModeNormal,
},
{
name: "migration json rollback",
enableStateMigration: true,
backend: StateStoreBackendJSON,
mode: StateStoreModeRollbackToJSON,
wantErr: ErrStateStoreFeatureUnavailable,
},
{
name: "migration bolt normal",
enableStateMigration: true,
backend: StateStoreBackendBolt,
mode: StateStoreModeNormal,
wantErr: ErrStateStoreFeatureUnavailable,
},
{
name: "migration bolt rollback",
enableStateMigration: true,
backend: StateStoreBackendBolt,
mode: StateStoreModeRollbackToJSON,
wantErr: ErrStateStoreFeatureUnavailable,
},
{
name: "migration enabled json normal",
enableBoltStateStore: true,
enableStateMigration: true,
backend: StateStoreBackendJSON,
mode: StateStoreModeNormal,
wantErr: ErrStateStoreFeatureUnavailable,
},
{
name: "migration enabled json rollback",
enableBoltStateStore: true,
enableStateMigration: true,
backend: StateStoreBackendJSON,
mode: StateStoreModeRollbackToJSON,
wantErr: ErrStateStoreFeatureUnavailable,
},
{
name: "migration enabled bolt normal",
enableBoltStateStore: true,
enableStateMigration: true,
backend: StateStoreBackendBolt,
mode: StateStoreModeNormal,
wantErr: ErrStateStoreFeatureUnavailable,
},
{
name: "migration enabled bolt rollback",
enableBoltStateStore: true,
enableStateMigration: true,
backend: StateStoreBackendBolt,
mode: StateStoreModeRollbackToJSON,
wantErr: ErrStateStoreFeatureUnavailable,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
config := CNSConfig{
EnableBoltStateStore: tt.enableBoltStateStore,
EnableStateMigration: tt.enableStateMigration,
StateStoreBackend: tt.backend,
StateStoreMode: tt.mode,
boolValues := []bool{false, true}
backends := []StateStoreBackend{StateStoreBackendJSON, StateStoreBackendBolt}
modes := []StateStoreMode{StateStoreModeNormal, StateStoreModeRollbackToJSON}
for _, enable := range boolValues {
for _, backend := range backends {
for _, mode := range modes {
for _, manage := range boolValues {
for _, debug := range boolValues {
for _, migrate := range boolValues {
for _, initializeFromCNI := range boolValues {
config := CNSConfig{
EnableBoltStateStore: enable,
EnablePersistentStateDebug: debug,
EnableStateMigration: migrate,
InitializeFromCNI: initializeFromCNI,
ManageEndpointState: manage,
StateStoreBackend: backend,
StateStoreMode: mode,
}
name := fmt.Sprintf(
"enable=%t/backend=%s/mode=%s/manage=%t/debug=%t/migrate=%t/from-cni=%t",
enable,
backend,
mode,
manage,
debug,
migrate,
initializeFromCNI,
)
t.Run(name, func(t *testing.T) {
wantErr := expectedStateStoreValidationError(config)
err := config.ValidateStateStore()
if wantErr == nil {
require.NoError(t, err)
return
}
require.ErrorIs(t, err, wantErr)
})
}
}
}
}
}
}
}
}

err := config.ValidateStateStore()
if tt.wantErr == nil {
require.NoError(t, err)
return
}
require.ErrorIs(t, err, tt.wantErr)
})
func expectedStateStoreValidationError(config CNSConfig) error {
if !config.EnableBoltStateStore {
if config.StateStoreBackend != StateStoreBackendJSON ||
config.StateStoreMode != StateStoreModeNormal ||
config.EnablePersistentStateDebug {
return ErrInvalidStateStoreConfig
}
return nil
}
switch {
case config.StateStoreBackend == StateStoreBackendBolt && config.StateStoreMode == StateStoreModeNormal:
if !config.ManageEndpointState {
return ErrInvalidStateStoreConfig
}
if config.EnableStateMigration && !config.InitializeFromCNI {
return ErrInvalidStateStoreConfig
}
return nil
case config.StateStoreBackend == StateStoreBackendJSON && config.StateStoreMode == StateStoreModeRollbackToJSON:
if !config.ManageEndpointState || config.EnablePersistentStateDebug {
return ErrInvalidStateStoreConfig
}
return nil
case config.StateStoreBackend == StateStoreBackendJSON && config.StateStoreMode == StateStoreModeNormal:
if config.EnablePersistentStateDebug {
return ErrInvalidStateStoreConfig
}
return nil
default:
return ErrInvalidStateStoreConfig
}
}

Expand All @@ -471,21 +457,20 @@ func TestCNSConfigValidateStateStoreDefaultsAndInvalidEnums(t *testing.T) {
config: CNSConfig{
EnableBoltStateStore: true,
},
wantErr: ErrStateStoreFeatureUnavailable,
},
{
name: "bolt with default mode",
config: CNSConfig{
StateStoreBackend: StateStoreBackendBolt,
},
wantErr: ErrStateStoreFeatureUnavailable,
wantErr: ErrInvalidStateStoreConfig,
},
{
name: "rollback with default backend",
config: CNSConfig{
StateStoreMode: StateStoreModeRollbackToJSON,
},
wantErr: ErrStateStoreFeatureUnavailable,
wantErr: ErrInvalidStateStoreConfig,
},
{
name: "invalid backend",
Expand Down
1 change: 1 addition & 0 deletions cns/configuration/testdata/good.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"ChannelMode": "Direct",
"EnableBoltStateStore": false,
"EnablePersistentStateDebug": false,
"InitializeFromCNI": true,
"StateStoreBackend": "json",
"StateStoreMode": "normal",
Expand Down
Loading
Loading