1// Copyright 2017 Istio 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/* 16Package component defines an in-memory representation of IstioOperator.<Feature>.<Component>. It provides functions 17for manipulating the component and rendering a manifest from it. 18See ../README.md for an architecture overview. 19*/ 20package component 21 22import ( 23 "fmt" 24 25 "github.com/ghodss/yaml" 26 27 "istio.io/api/operator/v1alpha1" 28 "istio.io/istio/operator/pkg/helm" 29 "istio.io/istio/operator/pkg/name" 30 "istio.io/istio/operator/pkg/patch" 31 "istio.io/istio/operator/pkg/tpath" 32 "istio.io/istio/operator/pkg/translate" 33 "istio.io/pkg/log" 34) 35 36const ( 37 // addonsChartDirName is the default subdir for all addon charts. 38 addonsChartDirName = "addons" 39 // String to emit for any component which is disabled. 40 componentDisabledStr = "component is disabled." 41 yamlCommentStr = "#" 42 43 // devDbg generates lots of output useful in development. 44 devDbg = false 45) 46 47var ( 48 scope = log.RegisterScope("installer", "installer", 0) 49) 50 51// Options defines options for a component. 52type Options struct { 53 // installSpec is the global IstioOperatorSpec. 54 InstallSpec *v1alpha1.IstioOperatorSpec 55 // translator is the translator for this component. 56 Translator *translate.Translator 57 // Namespace is the namespace for this component. 58 Namespace string 59} 60 61// IstioComponent defines the interface for a component. 62type IstioComponent interface { 63 // ComponentName returns the name of the component. 64 ComponentName() name.ComponentName 65 // ResourceName returns the name of the resources of the component. 66 ResourceName() string 67 // Namespace returns the namespace for the component. 68 Namespace() string 69 // Enabled reports whether the component is enabled. 70 Enabled() bool 71 // Run starts the component. Must me called before the component is used. 72 Run() error 73 // RenderManifest returns a string with the rendered manifest for the component. 74 RenderManifest() (string, error) 75} 76 77// CommonComponentFields is a struct common to all components. 78type CommonComponentFields struct { 79 *Options 80 ComponentName name.ComponentName 81 // addonName is the name of the addon component. 82 addonName string 83 // resourceName is the name of all resources for this component. 84 ResourceName string 85 // index is the index of the component (only used for components with multiple instances like gateways). 86 index int 87 // componentSpec for the actual component e.g. GatewaySpec, ComponentSpec. 88 componentSpec interface{} 89 // started reports whether the component is in initialized and running. 90 started bool 91 renderer helm.TemplateRenderer 92} 93 94// NewCoreComponent creates a new IstioComponent with the given componentName and options. 95func NewCoreComponent(cn name.ComponentName, opts *Options) IstioComponent { 96 var component IstioComponent 97 switch cn { 98 case name.IstioBaseComponentName: 99 component = NewCRDComponent(opts) 100 case name.PilotComponentName: 101 component = NewPilotComponent(opts) 102 case name.PolicyComponentName: 103 component = NewPolicyComponent(opts) 104 case name.TelemetryComponentName: 105 component = NewTelemetryComponent(opts) 106 case name.CNIComponentName: 107 component = NewCNIComponent(opts) 108 case name.IstiodRemoteComponentName: 109 component = NewIstiodRemoteComponent(opts) 110 default: 111 panic("Unknown component componentName: " + string(cn)) 112 } 113 return component 114} 115 116// BaseComponent is the base component. 117type BaseComponent struct { 118 *CommonComponentFields 119} 120 121// NewCRDComponent creates a new BaseComponent and returns a pointer to it. 122func NewCRDComponent(opts *Options) *BaseComponent { 123 return &BaseComponent{ 124 &CommonComponentFields{ 125 Options: opts, 126 ComponentName: name.IstioBaseComponentName, 127 }, 128 } 129} 130 131// Run implements the IstioComponent interface. 132func (c *BaseComponent) Run() error { 133 return runComponent(c.CommonComponentFields) 134} 135 136// RenderManifest implements the IstioComponent interface. 137func (c *BaseComponent) RenderManifest() (string, error) { 138 return renderManifest(c, c.CommonComponentFields) 139} 140 141// ComponentName implements the IstioComponent interface. 142func (c *BaseComponent) ComponentName() name.ComponentName { 143 return c.CommonComponentFields.ComponentName 144} 145 146// ResourceName implements the IstioComponent interface. 147func (c *BaseComponent) ResourceName() string { 148 return c.CommonComponentFields.ResourceName 149} 150 151// Namespace implements the IstioComponent interface. 152func (c *BaseComponent) Namespace() string { 153 return c.CommonComponentFields.Namespace 154} 155 156// Enabled implements the IstioComponent interface. 157func (c *BaseComponent) Enabled() bool { 158 return isCoreComponentEnabled(c.CommonComponentFields) 159} 160 161// PilotComponent is the pilot component. 162type PilotComponent struct { 163 *CommonComponentFields 164} 165 166// NewPilotComponent creates a new PilotComponent and returns a pointer to it. 167func NewPilotComponent(opts *Options) *PilotComponent { 168 cn := name.PilotComponentName 169 return &PilotComponent{ 170 &CommonComponentFields{ 171 Options: opts, 172 ComponentName: cn, 173 ResourceName: opts.Translator.ComponentMaps[cn].ResourceName, 174 }, 175 } 176} 177 178// Run implements the IstioComponent interface. 179func (c *PilotComponent) Run() error { 180 return runComponent(c.CommonComponentFields) 181} 182 183// RenderManifest implements the IstioComponent interface. 184func (c *PilotComponent) RenderManifest() (string, error) { 185 return renderManifest(c, c.CommonComponentFields) 186} 187 188// ComponentName implements the IstioComponent interface. 189func (c *PilotComponent) ComponentName() name.ComponentName { 190 return c.CommonComponentFields.ComponentName 191} 192 193// ResourceName implements the IstioComponent interface. 194func (c *PilotComponent) ResourceName() string { 195 return c.CommonComponentFields.ResourceName 196} 197 198// Namespace implements the IstioComponent interface. 199func (c *PilotComponent) Namespace() string { 200 return c.CommonComponentFields.Namespace 201} 202 203// Enabled implements the IstioComponent interface. 204func (c *PilotComponent) Enabled() bool { 205 return isCoreComponentEnabled(c.CommonComponentFields) 206} 207 208// PolicyComponent is the pilot component. 209type PolicyComponent struct { 210 *CommonComponentFields 211} 212 213// NewPolicyComponent creates a new PilotComponent and returns a pointer to it. 214func NewPolicyComponent(opts *Options) *PolicyComponent { 215 cn := name.PolicyComponentName 216 return &PolicyComponent{ 217 &CommonComponentFields{ 218 Options: opts, 219 ComponentName: cn, 220 }, 221 } 222} 223 224// Run implements the IstioComponent interface. 225func (c *PolicyComponent) Run() error { 226 return runComponent(c.CommonComponentFields) 227} 228 229// RenderManifest implements the IstioComponent interface. 230func (c *PolicyComponent) RenderManifest() (string, error) { 231 return renderManifest(c, c.CommonComponentFields) 232} 233 234// ComponentName implements the IstioComponent interface. 235func (c *PolicyComponent) ComponentName() name.ComponentName { 236 return c.CommonComponentFields.ComponentName 237} 238 239// ResourceName implements the IstioComponent interface. 240func (c *PolicyComponent) ResourceName() string { 241 return c.CommonComponentFields.ResourceName 242} 243 244// Namespace implements the IstioComponent interface. 245func (c *PolicyComponent) Namespace() string { 246 return c.CommonComponentFields.Namespace 247} 248 249// Enabled implements the IstioComponent interface. 250func (c *PolicyComponent) Enabled() bool { 251 return isCoreComponentEnabled(c.CommonComponentFields) 252} 253 254// TelemetryComponent is the pilot component. 255type TelemetryComponent struct { 256 *CommonComponentFields 257} 258 259// NewTelemetryComponent creates a new PilotComponent and returns a pointer to it. 260func NewTelemetryComponent(opts *Options) *TelemetryComponent { 261 cn := name.TelemetryComponentName 262 return &TelemetryComponent{ 263 &CommonComponentFields{ 264 Options: opts, 265 ComponentName: cn, 266 }, 267 } 268} 269 270// Run implements the IstioComponent interface. 271func (c *TelemetryComponent) Run() error { 272 return runComponent(c.CommonComponentFields) 273} 274 275// RenderManifest implements the IstioComponent interface. 276func (c *TelemetryComponent) RenderManifest() (string, error) { 277 return renderManifest(c, c.CommonComponentFields) 278} 279 280// ComponentName implements the IstioComponent interface. 281func (c *TelemetryComponent) ComponentName() name.ComponentName { 282 return c.CommonComponentFields.ComponentName 283} 284 285// ResourceName implements the IstioComponent interface. 286func (c *TelemetryComponent) ResourceName() string { 287 return c.CommonComponentFields.ResourceName 288} 289 290// Namespace implements the IstioComponent interface. 291func (c *TelemetryComponent) Namespace() string { 292 return c.CommonComponentFields.Namespace 293} 294 295// Enabled implements the IstioComponent interface. 296func (c *TelemetryComponent) Enabled() bool { 297 return isCoreComponentEnabled(c.CommonComponentFields) 298} 299 300// CNIComponent is the istio cni component. 301type CNIComponent struct { 302 *CommonComponentFields 303} 304 305// NewCNIComponent creates a new NewCNIComponent and returns a pointer to it. 306func NewCNIComponent(opts *Options) *CNIComponent { 307 cn := name.CNIComponentName 308 return &CNIComponent{ 309 &CommonComponentFields{ 310 Options: opts, 311 ComponentName: cn, 312 }, 313 } 314} 315 316// Run implements the IstioComponent interface. 317func (c *CNIComponent) Run() error { 318 return runComponent(c.CommonComponentFields) 319} 320 321// RenderManifest implements the IstioComponent interface. 322func (c *CNIComponent) RenderManifest() (string, error) { 323 return renderManifest(c, c.CommonComponentFields) 324} 325 326// ComponentName implements the IstioComponent interface. 327func (c *CNIComponent) ComponentName() name.ComponentName { 328 return c.CommonComponentFields.ComponentName 329} 330 331// ResourceName implements the IstioComponent interface. 332func (c *CNIComponent) ResourceName() string { 333 return c.CommonComponentFields.ResourceName 334} 335 336// Namespace implements the IstioComponent interface. 337func (c *CNIComponent) Namespace() string { 338 return c.CommonComponentFields.Namespace 339} 340 341// Enabled implements the IstioComponent interface. 342func (c *CNIComponent) Enabled() bool { 343 return isCoreComponentEnabled(c.CommonComponentFields) 344} 345 346// IstiodRemoteComponent is the istiod remote component. 347type IstiodRemoteComponent struct { 348 *CommonComponentFields 349} 350 351// NewIstiodRemoteComponent creates a new NewIstiodRemoteComponent and returns a pointer to it. 352func NewIstiodRemoteComponent(opts *Options) *IstiodRemoteComponent { 353 cn := name.IstiodRemoteComponentName 354 return &IstiodRemoteComponent{ 355 &CommonComponentFields{ 356 Options: opts, 357 ComponentName: cn, 358 }, 359 } 360} 361 362// Run implements the IstioComponent interface. 363func (c *IstiodRemoteComponent) Run() error { 364 return runComponent(c.CommonComponentFields) 365} 366 367// RenderManifest implements the IstioComponent interface. 368func (c *IstiodRemoteComponent) RenderManifest() (string, error) { 369 return renderManifest(c, c.CommonComponentFields) 370} 371 372// ComponentName implements the IstioComponent interface. 373func (c *IstiodRemoteComponent) ComponentName() name.ComponentName { 374 return c.CommonComponentFields.ComponentName 375} 376 377// ResourceName implements the IstioComponent interface. 378func (c *IstiodRemoteComponent) ResourceName() string { 379 return c.CommonComponentFields.ResourceName 380} 381 382// Namespace implements the IstioComponent interface. 383func (c *IstiodRemoteComponent) Namespace() string { 384 return c.CommonComponentFields.Namespace 385} 386 387// Enabled implements the IstioComponent interface. 388func (c *IstiodRemoteComponent) Enabled() bool { 389 return isCoreComponentEnabled(c.CommonComponentFields) 390} 391 392// IngressComponent is the ingress gateway component. 393type IngressComponent struct { 394 *CommonComponentFields 395} 396 397// NewIngressComponent creates a new IngressComponent and returns a pointer to it. 398func NewIngressComponent(resourceName string, index int, spec *v1alpha1.GatewaySpec, opts *Options) *IngressComponent { 399 cn := name.IngressComponentName 400 return &IngressComponent{ 401 CommonComponentFields: &CommonComponentFields{ 402 Options: opts, 403 ComponentName: cn, 404 ResourceName: resourceName, 405 index: index, 406 componentSpec: spec, 407 }, 408 } 409} 410 411// Run implements the IstioComponent interface. 412func (c *IngressComponent) Run() error { 413 return runComponent(c.CommonComponentFields) 414} 415 416// RenderManifest implements the IstioComponent interface. 417func (c *IngressComponent) RenderManifest() (string, error) { 418 return renderManifest(c, c.CommonComponentFields) 419} 420 421// ComponentName implements the IstioComponent interface. 422func (c *IngressComponent) ComponentName() name.ComponentName { 423 return c.CommonComponentFields.ComponentName 424} 425 426// ResourceName implements the IstioComponent interface. 427func (c *IngressComponent) ResourceName() string { 428 return c.CommonComponentFields.ResourceName 429} 430 431// Namespace implements the IstioComponent interface. 432func (c *IngressComponent) Namespace() string { 433 return c.CommonComponentFields.Namespace 434} 435 436// Enabled implements the IstioComponent interface. 437func (c *IngressComponent) Enabled() bool { 438 // type assert is guaranteed to work in this context. 439 return boolValue(c.componentSpec.(*v1alpha1.GatewaySpec).Enabled) 440} 441 442// EgressComponent is the egress gateway component. 443type EgressComponent struct { 444 *CommonComponentFields 445} 446 447// NewEgressComponent creates a new IngressComponent and returns a pointer to it. 448func NewEgressComponent(resourceName string, index int, spec *v1alpha1.GatewaySpec, opts *Options) *EgressComponent { 449 cn := name.EgressComponentName 450 return &EgressComponent{ 451 CommonComponentFields: &CommonComponentFields{ 452 Options: opts, 453 ComponentName: cn, 454 index: index, 455 componentSpec: spec, 456 ResourceName: resourceName, 457 }, 458 } 459} 460 461// Run implements the IstioComponent interface. 462func (c *EgressComponent) Run() error { 463 return runComponent(c.CommonComponentFields) 464} 465 466// RenderManifest implements the IstioComponent interface. 467func (c *EgressComponent) RenderManifest() (string, error) { 468 return renderManifest(c, c.CommonComponentFields) 469} 470 471// ComponentName implements the IstioComponent interface. 472func (c *EgressComponent) ComponentName() name.ComponentName { 473 return c.CommonComponentFields.ComponentName 474} 475 476// ResourceName implements the IstioComponent interface. 477func (c *EgressComponent) ResourceName() string { 478 return c.CommonComponentFields.ResourceName 479} 480 481// Namespace implements the IstioComponent interface. 482func (c *EgressComponent) Namespace() string { 483 return c.CommonComponentFields.Namespace 484} 485 486// Enabled implements the IstioComponent interface. 487func (c *EgressComponent) Enabled() bool { 488 // type assert is guaranteed to work in this context. 489 return boolValue(c.componentSpec.(*v1alpha1.GatewaySpec).Enabled) 490} 491 492// AddonComponent is an external component. 493type AddonComponent struct { 494 *CommonComponentFields 495} 496 497// NewAddonComponent creates a new IngressComponent and returns a pointer to it. 498func NewAddonComponent(addonName, resourceName string, spec *v1alpha1.ExternalComponentSpec, opts *Options) *AddonComponent { 499 return &AddonComponent{ 500 CommonComponentFields: &CommonComponentFields{ 501 Options: opts, 502 ComponentName: name.AddonComponentName, 503 ResourceName: resourceName, 504 addonName: addonName, 505 componentSpec: spec, 506 }, 507 } 508} 509 510// Run implements the IstioComponent interface. 511func (c *AddonComponent) Run() error { 512 return runComponent(c.CommonComponentFields) 513} 514 515// RenderManifest implements the IstioComponent interface. 516func (c *AddonComponent) RenderManifest() (string, error) { 517 return renderManifest(c, c.CommonComponentFields) 518} 519 520// ComponentName implements the IstioComponent interface. 521func (c *AddonComponent) ComponentName() name.ComponentName { 522 return c.CommonComponentFields.ComponentName 523} 524 525// ResourceName implements the IstioComponent interface. 526func (c *AddonComponent) ResourceName() string { 527 return c.CommonComponentFields.ResourceName 528} 529 530// Namespace implements the IstioComponent interface. 531func (c *AddonComponent) Namespace() string { 532 return c.CommonComponentFields.Namespace 533} 534 535// Enabled implements the IstioComponent interface. 536func (c *AddonComponent) Enabled() bool { 537 // type assert is guaranteed to work in this context. 538 return boolValue(c.componentSpec.(*v1alpha1.ExternalComponentSpec).Enabled) 539} 540 541// runComponent performs startup tasks for the component defined by the given CommonComponentFields. 542func runComponent(c *CommonComponentFields) error { 543 r, err := createHelmRenderer(c) 544 if err != nil { 545 return err 546 } 547 if err := r.Run(); err != nil { 548 return err 549 } 550 c.renderer = r 551 c.started = true 552 return nil 553} 554 555// renderManifest renders the manifest for the component defined by c and returns the resulting string. 556func renderManifest(c IstioComponent, cf *CommonComponentFields) (string, error) { 557 if !cf.started { 558 return "", fmt.Errorf("component %s not started in RenderManifest", cf.ComponentName) 559 } 560 561 if !c.Enabled() { 562 return disabledYAMLStr(cf.ComponentName, cf.ResourceName), nil 563 } 564 565 mergedYAML, err := cf.Translator.TranslateHelmValues(cf.InstallSpec, cf.componentSpec, cf.ComponentName) 566 if err != nil { 567 return "", err 568 } 569 570 log.Debugf("Merged values:\n%s\n", mergedYAML) 571 572 my, err := cf.renderer.RenderManifest(mergedYAML) 573 if err != nil { 574 log.Errorf("Error rendering the manifest: %s", err) 575 return "", err 576 } 577 my += helm.YAMLSeparator + "\n" 578 if devDbg { 579 scope.Infof("Initial manifest with merged values:\n%s\n", my) 580 } 581 // Add the k8s resources from IstioOperatorSpec. 582 my, err = cf.Translator.OverlayK8sSettings(my, cf.InstallSpec, cf.ComponentName, cf.ResourceName, cf.addonName, cf.index) 583 if err != nil { 584 return "", err 585 } 586 cnOutput := string(cf.ComponentName) 587 if !cf.ComponentName.IsCoreComponent() && !cf.ComponentName.IsGateway() { 588 cnOutput += " " + cf.addonName 589 } 590 my = "# Resources for " + cnOutput + " component\n\n" + my 591 if devDbg { 592 scope.Infof("Manifest after k8s API settings:\n%s\n", my) 593 } 594 // Add the k8s resource overlays from IstioOperatorSpec. 595 pathToK8sOverlay := "" 596 if !cf.ComponentName.IsCoreComponent() && !cf.ComponentName.IsGateway() { 597 pathToK8sOverlay += fmt.Sprintf("AddonComponents.%s.", cf.addonName) 598 } else { 599 pathToK8sOverlay += fmt.Sprintf("Components.%s.", cf.ComponentName) 600 if cf.ComponentName == name.IngressComponentName || cf.ComponentName == name.EgressComponentName { 601 pathToK8sOverlay += fmt.Sprintf("%d.", cf.index) 602 } 603 } 604 pathToK8sOverlay += "K8S.Overlays" 605 var overlays []*v1alpha1.K8SObjectOverlay 606 found, err := tpath.SetFromPath(cf.InstallSpec, pathToK8sOverlay, &overlays) 607 if err != nil { 608 return "", err 609 } 610 if !found { 611 log.Debugf("Manifest after resources: \n%s\n", my) 612 return my, nil 613 } 614 kyo, err := yaml.Marshal(overlays) 615 if err != nil { 616 return "", err 617 } 618 scope.Infof("Applying Kubernetes overlay: \n%s\n", kyo) 619 ret, err := patch.YAMLManifestPatch(my, cf.Namespace, overlays) 620 if err != nil { 621 return "", err 622 } 623 624 scope.Debugf("Manifest after resources and overlay: \n%s\n", ret) 625 return ret, nil 626} 627 628// createHelmRenderer creates a helm renderer for the component defined by c and returns a ptr to it. 629// If a helm subdir is not found in ComponentMap translations, it is assumed to be "addon/<component name>. 630func createHelmRenderer(c *CommonComponentFields) (helm.TemplateRenderer, error) { 631 iop := c.InstallSpec 632 cns := string(c.ComponentName) 633 if c.ComponentName.IsAddon() { 634 // For addons, distinguish the chart path using the addon name. 635 cns = c.addonName 636 } 637 helmSubdir := addonsChartDirName + "/" + cns 638 if cm := c.Translator.ComponentMap(cns); cm != nil { 639 helmSubdir = cm.HelmSubdir 640 } 641 return helm.NewHelmRenderer(iop.InstallPackagePath, helmSubdir, cns, c.Namespace) 642} 643 644func isCoreComponentEnabled(c *CommonComponentFields) bool { 645 enabled, err := c.Translator.IsComponentEnabled(c.ComponentName, c.InstallSpec) 646 if err != nil { 647 return false 648 } 649 return enabled 650} 651 652// disabledYAMLStr returns the YAML comment string that the given component is disabled. 653func disabledYAMLStr(componentName name.ComponentName, resourceName string) string { 654 fullName := string(componentName) 655 if resourceName != "" { 656 fullName += " " + resourceName 657 } 658 return fmt.Sprintf("%s %s %s\n", yamlCommentStr, fullName, componentDisabledStr) 659} 660 661// boolValue returns true is v is not nil and v.Value is true, or false otherwise. 662func boolValue(v *v1alpha1.BoolValueForPB) bool { 663 if v == nil { 664 return false 665 } 666 return v.Value 667} 668