diff --git a/hack/boilerplate.go.txt b/hack/boilerplate.go.txt index e69de29..4b76f1f 100644 --- a/hack/boilerplate.go.txt +++ b/hack/boilerplate.go.txt @@ -0,0 +1,15 @@ +/* +Copyright YEAR The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ diff --git a/pkg/api/v1alpha1/zz_generated.deepcopy.go b/pkg/api/v1alpha1/zz_generated.deepcopy.go index 830b40f..d0f8236 100644 --- a/pkg/api/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/api/v1alpha1/zz_generated.deepcopy.go @@ -1,6 +1,22 @@ //go:build !ignore_autogenerated // +build !ignore_autogenerated +/* +Copyright 2024 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + // Code generated by deepcopy-gen. DO NOT EDIT. package v1alpha1 diff --git a/pkg/config/config.go b/pkg/config/config.go index b807b2f..ea784be 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -9,7 +9,7 @@ import ( "github.com/hsbc/cost-manager/pkg/api/v1alpha1" "github.com/hsbc/cost-manager/pkg/controller" "k8s.io/apimachinery/pkg/runtime" - "k8s.io/kubectl/pkg/scheme" + "k8s.io/apimachinery/pkg/runtime/serializer" ) func Load(configFilePath string) (*v1alpha1.CostManagerConfiguration, error) { @@ -37,13 +37,24 @@ func Load(configFilePath string) (*v1alpha1.CostManagerConfiguration, error) { func decode(configData []byte) (*v1alpha1.CostManagerConfiguration, error) { config := &v1alpha1.CostManagerConfiguration{} - decoder := scheme.Codecs.UniversalDecoder(v1alpha1.SchemeGroupVersion) + // We enable strict decoding to ensure that we do not accept unknown fields + codecFactory := serializer.NewCodecFactory(runtime.NewScheme(), serializer.EnableStrict) + + decoder := codecFactory.UniversalDecoder(v1alpha1.SchemeGroupVersion) err := runtime.DecodeInto(decoder, configData, config) if err != nil { return nil, fmt.Errorf("failed to decode configuration: %s", err) } + // Verify that the API version and kind are what we expect + if config.APIVersion != v1alpha1.SchemeGroupVersion.String() { + return nil, fmt.Errorf("invalid API version: %s", config.APIVersion) + } + if config.Kind != "CostManagerConfiguration" { + return nil, fmt.Errorf("invalid kind: %s", config.Kind) + } + return config, nil } diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 1f9fda5..5022357 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -5,18 +5,96 @@ import ( "github.com/hsbc/cost-manager/pkg/api/v1alpha1" "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) func TestDecode(t *testing.T) { - configData := []byte(` + tests := map[string]struct { + configData []byte + valid bool + config *v1alpha1.CostManagerConfiguration + }{ + "validCloudProviderField": { + configData: []byte(` apiVersion: cost-manager.io/v1alpha1 kind: CostManagerConfiguration cloudProvider: name: gcp -`) - config, err := decode(configData) - require.Nil(t, err) - require.Equal(t, "gcp", config.CloudProvider.Name) +`), + valid: true, + config: &v1alpha1.CostManagerConfiguration{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "cost-manager.io/v1alpha1", + Kind: "CostManagerConfiguration", + }, + CloudProvider: v1alpha1.CloudProvider{ + Name: "gcp", + }, + }, + }, + "validControllersField": { + configData: []byte(` +apiVersion: cost-manager.io/v1alpha1 +kind: CostManagerConfiguration +controllers: +- spot-migrator +`), + valid: true, + config: &v1alpha1.CostManagerConfiguration{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "cost-manager.io/v1alpha1", + Kind: "CostManagerConfiguration", + }, + Controllers: []string{"spot-migrator"}, + }, + }, + "noFields": { + configData: []byte(` +apiVersion: cost-manager.io/v1alpha1 +kind: CostManagerConfiguration +`), + valid: true, + config: &v1alpha1.CostManagerConfiguration{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "cost-manager.io/v1alpha1", + Kind: "CostManagerConfiguration", + }, + }, + }, + "unknownAPIVersion": { + configData: []byte(` +apiVersion: foo.io/v1alpha1 +kind: CostManagerConfiguration +`), + valid: false, + }, + "unknownKind": { + configData: []byte(` +apiVersion: cost-manager.io/v1alpha1 +kind: FooConfiguration +`), + valid: false, + }, + "unknownField": { + configData: []byte(` +apiVersion: cost-manager.io/v1alpha1 +kind: CostManagerConfiguration +foo: bar +`), + valid: false, + }, + } + for name, test := range tests { + t.Run(name, func(t *testing.T) { + config, err := decode(test.configData) + if test.valid { + require.Nil(t, err) + require.Equal(t, test.config, config) + } else { + require.NotNil(t, err) + } + }) + } } func TestValidate(t *testing.T) {