github.com/mkimuram/operator-sdk@v0.7.1-0.20190410172100-52ad33a4bda0/internal/pkg/scaffold/olm-catalog/csv_updaters.go (about) 1 // Copyright 2018 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 catalog 16 17 import ( 18 "encoding/json" 19 "fmt" 20 "strings" 21 22 "github.com/ghodss/yaml" 23 olmapiv1alpha1 "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1" 24 olminstall "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/install" 25 appsv1 "k8s.io/api/apps/v1" 26 rbacv1 "k8s.io/api/rbac/v1" 27 apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" 28 ) 29 30 // CSVUpdater is an interface for any data that can be in a CSV, which will be 31 // set to the corresponding field on Apply(). 32 type CSVUpdater interface { 33 // Apply applies a data update to a CSV argument. 34 Apply(*olmapiv1alpha1.ClusterServiceVersion) error 35 } 36 37 type updaterStore struct { 38 installStrategy *InstallStrategyUpdate 39 crds *CustomResourceDefinitionsUpdate 40 almExamples *ALMExamplesUpdate 41 } 42 43 func NewUpdaterStore() *updaterStore { 44 return &updaterStore{ 45 installStrategy: &InstallStrategyUpdate{ 46 &olminstall.StrategyDetailsDeployment{}, 47 }, 48 crds: &CustomResourceDefinitionsUpdate{ 49 &olmapiv1alpha1.CustomResourceDefinitions{}, 50 make(map[string]struct{}), 51 }, 52 almExamples: &ALMExamplesUpdate{}, 53 } 54 } 55 56 // Apply iteratively calls each stored CSVUpdater's Apply() method. 57 func (s *updaterStore) Apply(csv *olmapiv1alpha1.ClusterServiceVersion) error { 58 updaters := []CSVUpdater{s.installStrategy, s.crds, s.almExamples} 59 for _, updater := range updaters { 60 if err := updater.Apply(csv); err != nil { 61 return err 62 } 63 } 64 return nil 65 } 66 67 func getKindfromYAML(yamlData []byte) (string, error) { 68 var temp struct { 69 Kind string 70 } 71 if err := yaml.Unmarshal(yamlData, &temp); err != nil { 72 return "", err 73 } 74 return temp.Kind, nil 75 } 76 77 func (s *updaterStore) AddToUpdater(yamlSpec []byte, kind string) (found bool, err error) { 78 found = true 79 switch kind { 80 case "Role": 81 err = s.AddRole(yamlSpec) 82 case "ClusterRole": 83 err = s.AddClusterRole(yamlSpec) 84 case "Deployment": 85 err = s.AddDeploymentSpec(yamlSpec) 86 case "CustomResourceDefinition": 87 // All CRD's present will be 'owned'. 88 err = s.AddOwnedCRD(yamlSpec) 89 default: 90 found = false 91 } 92 return found, err 93 } 94 95 type InstallStrategyUpdate struct { 96 *olminstall.StrategyDetailsDeployment 97 } 98 99 func (store *updaterStore) AddRole(yamlDoc []byte) error { 100 role := &rbacv1.Role{} 101 if err := yaml.Unmarshal(yamlDoc, role); err != nil { 102 return err 103 } 104 perm := olminstall.StrategyDeploymentPermissions{ 105 ServiceAccountName: role.ObjectMeta.Name, 106 Rules: role.Rules, 107 } 108 store.installStrategy.Permissions = append(store.installStrategy.Permissions, perm) 109 110 return nil 111 } 112 113 func (store *updaterStore) AddClusterRole(yamlDoc []byte) error { 114 clusterRole := &rbacv1.ClusterRole{} 115 if err := yaml.Unmarshal(yamlDoc, clusterRole); err != nil { 116 return err 117 } 118 perm := olminstall.StrategyDeploymentPermissions{ 119 ServiceAccountName: clusterRole.ObjectMeta.Name, 120 Rules: clusterRole.Rules, 121 } 122 store.installStrategy.ClusterPermissions = append(store.installStrategy.ClusterPermissions, perm) 123 124 return nil 125 } 126 127 func (store *updaterStore) AddDeploymentSpec(yamlDoc []byte) error { 128 dep := &appsv1.Deployment{} 129 if err := yaml.Unmarshal(yamlDoc, dep); err != nil { 130 return err 131 } 132 depSpec := olminstall.StrategyDeploymentSpec{ 133 Name: dep.ObjectMeta.Name, 134 Spec: dep.Spec, 135 } 136 store.installStrategy.DeploymentSpecs = append(store.installStrategy.DeploymentSpecs, depSpec) 137 138 return nil 139 } 140 141 func (u *InstallStrategyUpdate) Apply(csv *olmapiv1alpha1.ClusterServiceVersion) (err error) { 142 // Get install strategy from csv. Default to a deployment strategy if none found. 143 var strat olminstall.Strategy 144 if csv.Spec.InstallStrategy.StrategyName == "" { 145 csv.Spec.InstallStrategy.StrategyName = olminstall.InstallStrategyNameDeployment 146 strat = &olminstall.StrategyDetailsDeployment{} 147 } else { 148 var resolver *olminstall.StrategyResolver 149 strat, err = resolver.UnmarshalStrategy(csv.Spec.InstallStrategy) 150 if err != nil { 151 return err 152 } 153 } 154 155 switch s := strat.(type) { 156 case *olminstall.StrategyDetailsDeployment: 157 // Update permissions and deployments. 158 u.updatePermissions(s) 159 u.updateClusterPermissions(s) 160 u.updateDeploymentSpecs(s) 161 default: 162 return fmt.Errorf("install strategy (%v) of unknown type", strat) 163 } 164 165 // Re-serialize permissions into csv strategy. 166 updatedStrat, err := json.Marshal(strat) 167 if err != nil { 168 return err 169 } 170 csv.Spec.InstallStrategy.StrategySpecRaw = updatedStrat 171 172 return nil 173 } 174 175 func (u *InstallStrategyUpdate) updatePermissions(strat *olminstall.StrategyDetailsDeployment) { 176 if len(u.Permissions) != 0 { 177 strat.Permissions = u.Permissions 178 } 179 } 180 181 func (u *InstallStrategyUpdate) updateClusterPermissions(strat *olminstall.StrategyDetailsDeployment) { 182 if len(u.ClusterPermissions) != 0 { 183 strat.ClusterPermissions = u.ClusterPermissions 184 } 185 } 186 187 func (u *InstallStrategyUpdate) updateDeploymentSpecs(strat *olminstall.StrategyDetailsDeployment) { 188 if len(u.DeploymentSpecs) != 0 { 189 strat.DeploymentSpecs = u.DeploymentSpecs 190 } 191 } 192 193 type CustomResourceDefinitionsUpdate struct { 194 *olmapiv1alpha1.CustomResourceDefinitions 195 crKinds map[string]struct{} 196 } 197 198 func (store *updaterStore) AddOwnedCRD(yamlDoc []byte) error { 199 crd := &apiextv1beta1.CustomResourceDefinition{} 200 if err := yaml.Unmarshal(yamlDoc, crd); err != nil { 201 return err 202 } 203 store.crds.Owned = append(store.crds.Owned, olmapiv1alpha1.CRDDescription{ 204 Name: crd.ObjectMeta.Name, 205 Version: crd.Spec.Version, 206 Kind: crd.Spec.Names.Kind, 207 }) 208 store.crds.crKinds[crd.Spec.Names.Kind] = struct{}{} 209 return nil 210 } 211 212 // Apply updates csv's "owned" CRDDescriptions. "required" CRDDescriptions are 213 // left as-is, since they are user-defined values. 214 func (u *CustomResourceDefinitionsUpdate) Apply(csv *olmapiv1alpha1.ClusterServiceVersion) error { 215 set := make(map[string]olmapiv1alpha1.CRDDescription) 216 for _, csvDesc := range csv.Spec.CustomResourceDefinitions.Owned { 217 set[csvDesc.Name] = csvDesc 218 } 219 du := u.DeepCopy() 220 for i, uDesc := range u.Owned { 221 if csvDesc, ok := set[uDesc.Name]; ok { 222 csvDesc.Name = uDesc.Name 223 csvDesc.Version = uDesc.Version 224 csvDesc.Kind = uDesc.Kind 225 du.Owned[i] = csvDesc 226 } 227 } 228 csv.Spec.CustomResourceDefinitions.Owned = du.Owned 229 return nil 230 } 231 232 type ALMExamplesUpdate struct { 233 crs []string 234 } 235 236 func (store *updaterStore) AddCR(yamlDoc []byte) error { 237 if len(yamlDoc) == 0 { 238 return nil 239 } 240 crBytes, err := yaml.YAMLToJSON(yamlDoc) 241 if err != nil { 242 return err 243 } 244 store.almExamples.crs = append(store.almExamples.crs, string(crBytes)) 245 return nil 246 } 247 248 func (u *ALMExamplesUpdate) Apply(csv *olmapiv1alpha1.ClusterServiceVersion) error { 249 if len(u.crs) == 0 { 250 return nil 251 } 252 if csv.GetAnnotations() == nil { 253 csv.SetAnnotations(make(map[string]string)) 254 } 255 sb := &strings.Builder{} 256 sb.WriteString(`[`) 257 for i, example := range u.crs { 258 sb.WriteString(example) 259 if i < len(u.crs)-1 { 260 sb.WriteString(`,`) 261 } 262 } 263 sb.WriteString(`]`) 264 265 csv.GetAnnotations()["alm-examples"] = sb.String() 266 return nil 267 }