diff --git a/cmd/webhook/app/webhook.go b/cmd/webhook/app/webhook.go index 8945b07b6..57dfb5b04 100644 --- a/cmd/webhook/app/webhook.go +++ b/cmd/webhook/app/webhook.go @@ -253,7 +253,7 @@ func loadConfigFile(name string) (*config.WebhookConfiguration, error) { if err != nil { return nil, fmt.Errorf(errFmt, name, err) } - loader, err := configfile.NewFSLoader(webhookConfigFile) + loader, err := configfile.NewFSLoader(configfile.NewRealFS(), webhookConfigFile) if err != nil { return nil, fmt.Errorf(errFmt, name, err) } diff --git a/pkg/webhook/configfile/BUILD.bazel b/pkg/webhook/configfile/BUILD.bazel index 66c7c3594..763c62dbc 100644 --- a/pkg/webhook/configfile/BUILD.bazel +++ b/pkg/webhook/configfile/BUILD.bazel @@ -1,4 +1,4 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "go_default_library", @@ -25,3 +25,9 @@ filegroup( tags = ["automanaged"], visibility = ["//visibility:public"], ) + +go_test( + name = "go_default_test", + srcs = ["configfile_test.go"], + embed = [":go_default_library"], +) diff --git a/pkg/webhook/configfile/configfile.go b/pkg/webhook/configfile/configfile.go index 4d9f70cc6..f7a31996d 100644 --- a/pkg/webhook/configfile/configfile.go +++ b/pkg/webhook/configfile/configfile.go @@ -27,11 +27,28 @@ import ( "github.com/jetstack/cert-manager/internal/apis/config/scheme" ) +// Filesystem is an interface used to mock out calls to ReadFile +type Filesystem interface { + ReadFile(filename string) ([]byte, error) +} + +type realFS struct{} + +func (fs realFS) ReadFile(filename string) ([]byte, error) { + return ioutil.ReadFile(filename) +} + +// NewRealFS builds a Filesystem that wraps around `ioutil.ReadFile`. +func NewRealFS() Filesystem { + return realFS{} +} + type Loader interface { Load() (*config.WebhookConfiguration, error) } type fsLoader struct { + fs Filesystem filename string codec *serializer.CodecFactory } @@ -39,7 +56,7 @@ type fsLoader struct { var _ Loader = &fsLoader{} func (f *fsLoader) Load() (*config.WebhookConfiguration, error) { - data, err := ioutil.ReadFile(f.filename) + data, err := f.fs.ReadFile(f.filename) if err != nil { return nil, fmt.Errorf("failed to read webhook config file %q, error: %v", f.filename, err) } @@ -58,13 +75,14 @@ func (f *fsLoader) Load() (*config.WebhookConfiguration, error) { return cfg, nil } -func NewFSLoader(name string) (Loader, error) { +func NewFSLoader(fs Filesystem, name string) (Loader, error) { _, webhookCodec, err := scheme.NewSchemeAndCodecs(serializer.EnableStrict) if err != nil { return nil, err } return &fsLoader{ + fs: fs, filename: name, codec: webhookCodec, }, nil diff --git a/pkg/webhook/configfile/configfile_test.go b/pkg/webhook/configfile/configfile_test.go new file mode 100644 index 000000000..29e2019e6 --- /dev/null +++ b/pkg/webhook/configfile/configfile_test.go @@ -0,0 +1,63 @@ +/* +Copyright 2021 The cert-manager 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. +*/ + +package configfile + +import ( + "fmt" + "testing" +) + +func TestFSLoader_Load(t *testing.T) { + const expectedFilename = "/path/to/config/file" + const kubeConfigPath = "path/to/kubeconfig/file" + + loader, err := NewFSLoader(newFakeFS(func(filename string) ([]byte, error) { + if filename != expectedFilename { + t.Fatalf("unexpected filename %q passed to ReadFile", filename) + return nil, fmt.Errorf("unexpected filename %q", filename) + } + return []byte(fmt.Sprintf(`apiVersion: config.cert-manager.io/v1alpha1 +kind: WebhookConfiguration +kubeConfig: %s`, kubeConfigPath)), nil + }), expectedFilename) + if err != nil { + t.Fatal(err) + } + + cfg, err := loader.Load() + if err != nil { + t.Fatal(err) + } + + // the config loader will force paths to be 'absolute' if they are provided as relative. + absKubeConfigPath := "/path/to/config/path/to/kubeconfig/file" + if cfg.KubeConfig != absKubeConfigPath { + t.Errorf("expected kubeConfig to be set to %q but got %q", absKubeConfigPath, cfg.KubeConfig) + } +} + +func newFakeFS(readFileFunc func(string) ([]byte, error)) Filesystem { + return fakeFS{readFileFunc: readFileFunc} +} + +type fakeFS struct { + readFileFunc func(string) ([]byte, error) +} + +func (f fakeFS) ReadFile(filename string) ([]byte, error) { + return f.readFileFunc(filename) +}