sigs.k8s.io/cluster-api-provider-aws@v1.5.5/cmd/clusterawsadm/configreader/configreader.go (about) 1 /* 2 Copyright 2020 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package configreader 18 19 import ( 20 "fmt" 21 "os" 22 "path/filepath" 23 24 "github.com/pkg/errors" 25 "k8s.io/apimachinery/pkg/runtime" 26 "k8s.io/apimachinery/pkg/runtime/serializer" 27 yamlserializer "k8s.io/apimachinery/pkg/runtime/serializer/yaml" 28 29 bootstrapv1 "sigs.k8s.io/cluster-api-provider-aws/cmd/clusterawsadm/api/bootstrap/v1beta1" 30 bootstrapschemev1 "sigs.k8s.io/cluster-api-provider-aws/cmd/clusterawsadm/api/bootstrap/v1beta1/scheme" 31 ) 32 33 type errEmptyBootstrapConfig string 34 35 func (e errEmptyBootstrapConfig) Error() string { 36 return fmt.Sprintf("bootstrap config file %q was empty", string(e)) 37 } 38 39 // LoadConfigFile loads a YAML file representing a bootstrapv1.AWSIAMConfiguration. 40 func LoadConfigFile(name string) (*bootstrapv1.AWSIAMConfiguration, error) { 41 // compute absolute path based on current working dir 42 iamConfigFile, err := filepath.Abs(name) 43 if err != nil { 44 return nil, fmt.Errorf("failed to convert IAM config path into absolute path %s, error: %w", name, err) 45 } 46 loader, err := newFsLoader(iamConfigFile) 47 if err != nil { 48 return nil, fmt.Errorf("failed to initialize filesystem loader: %w", err) 49 } 50 return loader.Load() 51 } 52 53 // Loader loads configuration from a storage layer. 54 type loader interface { 55 // Load loads and returns the AWSIAMConfiguration from the storage layer, or an error if a configuration could not be loaded. 56 Load() (*bootstrapv1.AWSIAMConfiguration, error) 57 } 58 59 // fsLoader loads configuration from `configDir`.. 60 type fsLoader struct { 61 62 // bootstrapCodecs is the scheme used to decode config files 63 bootstrapCodecs *serializer.CodecFactory 64 // bootstrapFile is an absolute path to the file containing a serialized KubeletConfiguration 65 bootstrapFile string 66 } 67 68 // ReadFile reads a file. 69 func (fsLoader) ReadFile(filename string) ([]byte, error) { 70 return os.ReadFile(filepath.Clean(filename)) 71 } 72 73 // NewFsLoader returns a Loader that loads a AWSIAMConfiguration from the `config file`. 74 func newFsLoader(bootstrapFile string) (loader, error) { 75 _, bootstrapCodecs, err := bootstrapschemev1.NewSchemeAndCodecs() 76 77 if err != nil { 78 return nil, err 79 } 80 81 return &fsLoader{ 82 bootstrapCodecs: bootstrapCodecs, 83 bootstrapFile: bootstrapFile, 84 }, nil 85 } 86 87 func (loader *fsLoader) Load() (*bootstrapv1.AWSIAMConfiguration, error) { 88 data, err := loader.ReadFile(loader.bootstrapFile) 89 if err != nil { 90 return nil, fmt.Errorf("failed to read bootstrap config file %q, error: %w", loader.bootstrapFile, err) 91 } 92 93 // no configuration is an error, some parameters are required 94 if len(data) == 0 { 95 return nil, errEmptyBootstrapConfig(loader.bootstrapFile) 96 } 97 98 // Deserialize the TypeMeta information of this byte slice 99 gvk, err := yamlserializer.DefaultMetaFactory.Interpret(data) 100 if err != nil { 101 return nil, err 102 } 103 104 if len(gvk.Group) == 0 || len(gvk.Version) == 0 || len(gvk.Kind) == 0 { 105 return nil, errors.Errorf("invalid configuration for GroupVersionKind %+v: kind and apiVersion is mandatory information that must be specified", gvk) 106 } 107 108 kc, err := DecodeBootstrapConfiguration(loader.bootstrapCodecs, data) 109 if err != nil { 110 return nil, err 111 } 112 113 fileDir := filepath.Dir(loader.bootstrapFile) 114 115 // make all paths absolute 116 resolveRelativePaths([]*string{&fileDir}, "") 117 return kc, nil 118 } 119 120 // resolveRelativePaths makes relative paths absolute by resolving them against `root`. 121 func resolveRelativePaths(paths []*string, root string) { 122 for _, path := range paths { 123 // leave empty paths alone, "no path" is a valid input 124 // do not attempt to resolve paths that are already absolute 125 if len(*path) > 0 && !filepath.IsAbs(*path) { 126 *path = filepath.Join(root, *path) 127 } 128 } 129 } 130 131 // DecodeBootstrapConfiguration decodes a serialized AWSIAMConfiguration to the internal type. 132 func DecodeBootstrapConfiguration(bootstrapCodecs *serializer.CodecFactory, data []byte) (*bootstrapv1.AWSIAMConfiguration, error) { 133 obj := &bootstrapv1.AWSIAMConfiguration{} 134 135 if err := runtime.DecodeInto(bootstrapCodecs.UniversalDecoder(), data, obj); err != nil { 136 return nil, errors.Wrap(err, "error decoding metadata.yaml") 137 } 138 139 bootstrapv1.SetDefaults_AWSIAMConfiguration(obj) 140 141 return obj, nil 142 }