// Copyright 2021 Google LLC // // 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 kubeconfig import ( "errors" "fmt" "io" "sigs.k8s.io/kustomize/kyaml/yaml" ) type ReadWriteResetCloser interface { io.ReadWriteCloser // Reset truncates the file and seeks to the beginning of the file. Reset() error } type Loader interface { Load() ([]ReadWriteResetCloser, error) } type Kubeconfig struct { loader Loader f ReadWriteResetCloser config *yaml.RNode } func (k *Kubeconfig) WithLoader(l Loader) *Kubeconfig { k.loader = l return k } func (k *Kubeconfig) Close() error { if k.f == nil { return nil } return k.f.Close() } func (k *Kubeconfig) Parse() error { files, err := k.loader.Load() if err != nil { return fmt.Errorf("failed to load: %w", err) } // TODO since we don't support multiple kubeconfig files at the moment, there's just 1 file f := files[0] k.f = f var v yaml.Node if err := yaml.NewDecoder(f).Decode(&v); err != nil { return fmt.Errorf("failed to decode: %w", err) } k.config = yaml.NewRNode(&v) if k.config.YNode().Kind != yaml.MappingNode { return errors.New("kubeconfig file is not a map document") } return nil } func (k *Kubeconfig) Bytes() ([]byte, error) { str, err := k.config.String() if err != nil { return nil, err } return []byte(str), nil } func (k *Kubeconfig) Save() error { if err := k.f.Reset(); err != nil { return fmt.Errorf("failed to reset file: %w", err) } enc := yaml.NewEncoder(k.f) enc.SetIndent(0) if err := enc.Encode(k.config.YNode()); err != nil { return err } return enc.Close() }