github.imxd.top/operator-framework/operator-sdk@v0.8.2/pkg/helm/watches/watches.go (about) 1 // Copyright 2019 The Operator-SDK Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package watches 16 17 import ( 18 "errors" 19 "fmt" 20 "io/ioutil" 21 22 yaml "gopkg.in/yaml.v2" 23 "k8s.io/apimachinery/pkg/runtime/schema" 24 "k8s.io/helm/pkg/chartutil" 25 ) 26 27 // Watch defines options for configuring a watch for a Helm-based 28 // custom resource. 29 type Watch struct { 30 GroupVersionKind schema.GroupVersionKind 31 ChartDir string 32 WatchDependentResources bool 33 } 34 35 type yamlWatch struct { 36 Group string `yaml:"group"` 37 Version string `yaml:"version"` 38 Kind string `yaml:"kind"` 39 Chart string `yaml:"chart"` 40 WatchDependentResources bool `yaml:"watchDependentResources"` 41 } 42 43 func (w *yamlWatch) UnmarshalYAML(unmarshal func(interface{}) error) error { 44 // by default, the operator will watch dependent resources 45 w.WatchDependentResources = true 46 47 // hide watch data in plain struct to prevent unmarshal from calling 48 // UnmarshalYAML again 49 type plain yamlWatch 50 51 return unmarshal((*plain)(w)) 52 } 53 54 // Load loads a slice of Watches from the watch file at `path`. For each entry 55 // in the watches file, it verifies the configuration. If an error is 56 // encountered loading the file or verifying the configuration, it will be 57 // returned. 58 func Load(path string) ([]Watch, error) { 59 b, err := ioutil.ReadFile(path) 60 if err != nil { 61 return nil, err 62 } 63 64 yamlWatches := []yamlWatch{} 65 err = yaml.Unmarshal(b, &yamlWatches) 66 if err != nil { 67 return nil, err 68 } 69 70 watches := []Watch{} 71 watchesMap := make(map[schema.GroupVersionKind]Watch) 72 for _, w := range yamlWatches { 73 gvk := schema.GroupVersionKind{ 74 Group: w.Group, 75 Version: w.Version, 76 Kind: w.Kind, 77 } 78 79 if err := verifyGVK(gvk); err != nil { 80 return nil, fmt.Errorf("invalid GVK: %s: %s", gvk, err) 81 } 82 83 if _, err := chartutil.IsChartDir(w.Chart); err != nil { 84 return nil, fmt.Errorf("invalid chart directory %s: %s", w.Chart, err) 85 } 86 87 if _, ok := watchesMap[gvk]; ok { 88 return nil, fmt.Errorf("duplicate GVK: %s", gvk) 89 } 90 watch := Watch{ 91 GroupVersionKind: gvk, 92 ChartDir: w.Chart, 93 WatchDependentResources: w.WatchDependentResources, 94 } 95 watchesMap[gvk] = watch 96 watches = append(watches, watch) 97 } 98 return watches, nil 99 } 100 101 func verifyGVK(gvk schema.GroupVersionKind) error { 102 // A GVK without a group is valid. Certain scenarios may cause a GVK 103 // without a group to fail in other ways later in the initialization 104 // process. 105 if gvk.Version == "" { 106 return errors.New("version must not be empty") 107 } 108 if gvk.Kind == "" { 109 return errors.New("kind must not be empty") 110 } 111 return nil 112 }