github.com/coreos/mantle@v0.13.0/platform/conf/conf.go (about) 1 // Copyright 2016-2018 CoreOS, Inc. 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 conf 16 17 import ( 18 "encoding/json" 19 "fmt" 20 "io/ioutil" 21 "net/url" 22 "os" 23 "reflect" 24 "strings" 25 26 ct "github.com/coreos/container-linux-config-transpiler/config" 27 cci "github.com/coreos/coreos-cloudinit/config" 28 ignerr "github.com/coreos/ignition/config/shared/errors" 29 v1 "github.com/coreos/ignition/config/v1" 30 v1types "github.com/coreos/ignition/config/v1/types" 31 v2 "github.com/coreos/ignition/config/v2_0" 32 v2types "github.com/coreos/ignition/config/v2_0/types" 33 v21 "github.com/coreos/ignition/config/v2_1" 34 v21types "github.com/coreos/ignition/config/v2_1/types" 35 v22 "github.com/coreos/ignition/config/v2_2" 36 v22types "github.com/coreos/ignition/config/v2_2/types" 37 v23 "github.com/coreos/ignition/config/v2_3" 38 v23types "github.com/coreos/ignition/config/v2_3/types" 39 ignvalidate "github.com/coreos/ignition/config/validate" 40 ign3err "github.com/coreos/ignition/v2/config/shared/errors" 41 v3 "github.com/coreos/ignition/v2/config/v3_0" 42 v3types "github.com/coreos/ignition/v2/config/v3_0/types" 43 ign3validate "github.com/coreos/ignition/v2/config/validate" 44 "github.com/coreos/pkg/capnslog" 45 "github.com/vincent-petithory/dataurl" 46 "golang.org/x/crypto/ssh/agent" 47 ) 48 49 type kind int 50 51 const ( 52 kindEmpty kind = iota 53 kindCloudConfig 54 kindIgnition 55 kindContainerLinuxConfig 56 kindScript 57 ) 58 59 var plog = capnslog.NewPackageLogger("github.com/coreos/mantle", "platform/conf") 60 61 // UserData is an immutable, unvalidated configuration for a Container Linux 62 // machine. 63 type UserData struct { 64 kind kind 65 data string 66 extraKeys []*agent.Key // SSH keys to be injected during rendering 67 } 68 69 // Conf is a configuration for a Container Linux machine. It may be either a 70 // coreos-cloudconfig or an ignition configuration. 71 type Conf struct { 72 ignitionV1 *v1types.Config 73 ignitionV2 *v2types.Config 74 ignitionV21 *v21types.Config 75 ignitionV22 *v22types.Config 76 ignitionV23 *v23types.Config 77 ignitionV3 *v3types.Config 78 cloudconfig *cci.CloudConfig 79 script string 80 } 81 82 func Empty() *UserData { 83 return &UserData{ 84 kind: kindEmpty, 85 } 86 } 87 88 func ContainerLinuxConfig(data string) *UserData { 89 return &UserData{ 90 kind: kindContainerLinuxConfig, 91 data: data, 92 } 93 } 94 95 func Ignition(data string) *UserData { 96 return &UserData{ 97 kind: kindIgnition, 98 data: data, 99 } 100 } 101 102 func CloudConfig(data string) *UserData { 103 return &UserData{ 104 kind: kindCloudConfig, 105 data: data, 106 } 107 } 108 109 func Script(data string) *UserData { 110 return &UserData{ 111 kind: kindScript, 112 data: data, 113 } 114 } 115 116 func Unknown(data string) *UserData { 117 u := &UserData{ 118 data: data, 119 } 120 121 _, _, err := v22.Parse([]byte(data)) 122 switch err { 123 case ignerr.ErrEmpty: 124 u.kind = kindEmpty 125 case ignerr.ErrCloudConfig: 126 u.kind = kindCloudConfig 127 case ignerr.ErrScript: 128 u.kind = kindScript 129 default: 130 // Guess whether this is an Ignition config or a CLC. 131 // This treats an invalid Ignition config as a CLC, and a 132 // CLC in the JSON subset of YAML as an Ignition config. 133 var decoded interface{} 134 if err := json.Unmarshal([]byte(data), &decoded); err != nil { 135 u.kind = kindContainerLinuxConfig 136 } else { 137 u.kind = kindIgnition 138 } 139 } 140 141 return u 142 } 143 144 // Contains returns true if the UserData contains the specified string. 145 func (u *UserData) Contains(substr string) bool { 146 return strings.Contains(u.data, substr) 147 } 148 149 // Performs a string substitution and returns a new UserData. 150 func (u *UserData) Subst(old, new string) *UserData { 151 ret := *u 152 ret.data = strings.Replace(u.data, old, new, -1) 153 return &ret 154 } 155 156 // Adds an SSH key and returns a new UserData. 157 func (u *UserData) AddKey(key agent.Key) *UserData { 158 ret := *u 159 ret.extraKeys = append(ret.extraKeys, &key) 160 return &ret 161 } 162 163 func (u *UserData) IsIgnitionCompatible() bool { 164 return u.kind == kindIgnition || u.kind == kindContainerLinuxConfig 165 } 166 167 // Render parses userdata and returns a new Conf. It returns an error if the 168 // userdata can't be parsed. 169 func (u *UserData) Render(ctPlatform string) (*Conf, error) { 170 c := &Conf{} 171 172 renderIgnition := func() error { 173 // Try each known version in turn. Newer parsers will 174 // fall back to older ones, so try older versions first. 175 ignc1, report, err := v1.Parse([]byte(u.data)) 176 if err == nil { 177 c.ignitionV1 = &ignc1 178 return nil 179 } else if err != ignerr.ErrUnknownVersion { 180 plog.Errorf("invalid userdata: %v", report) 181 return err 182 } 183 184 ignc2, report, err := v2.Parse([]byte(u.data)) 185 if err == nil { 186 c.ignitionV2 = &ignc2 187 return nil 188 } else if err != ignerr.ErrUnknownVersion { 189 plog.Errorf("invalid userdata: %v", report) 190 return err 191 } 192 193 ignc21, report, err := v21.Parse([]byte(u.data)) 194 if err == nil { 195 c.ignitionV21 = &ignc21 196 return nil 197 } else if err != ignerr.ErrUnknownVersion { 198 plog.Errorf("invalid userdata: %v", report) 199 return err 200 } 201 202 ignc22, report, err := v22.Parse([]byte(u.data)) 203 if err == nil { 204 c.ignitionV22 = &ignc22 205 return nil 206 } else if err != ignerr.ErrUnknownVersion { 207 plog.Errorf("invalid userdata: %v", report) 208 return err 209 } 210 211 ignc23, report, err := v23.Parse([]byte(u.data)) 212 if err == nil { 213 c.ignitionV23 = &ignc23 214 return nil 215 } else if err != ignerr.ErrUnknownVersion { 216 plog.Errorf("invalid userdata: %v", report) 217 return err 218 } 219 220 ignc3, report3, err := v3.Parse([]byte(u.data)) 221 if err == nil { 222 c.ignitionV3 = &ignc3 223 return nil 224 } else if err != ign3err.ErrUnknownVersion { 225 plog.Errorf("invalid userdata: %v", report3) 226 return err 227 } 228 229 // give up 230 return err 231 } 232 233 switch u.kind { 234 case kindEmpty: 235 // empty, noop 236 case kindCloudConfig: 237 var err error 238 c.cloudconfig, err = cci.NewCloudConfig(u.data) 239 if err != nil { 240 return nil, err 241 } 242 case kindScript: 243 // pass through scripts unmodified, you are on your own. 244 c.script = u.data 245 case kindIgnition: 246 err := renderIgnition() 247 if err != nil { 248 return nil, err 249 } 250 case kindContainerLinuxConfig: 251 clc, ast, report := ct.Parse([]byte(u.data)) 252 if report.IsFatal() { 253 return nil, fmt.Errorf("parsing Container Linux config: %s", report) 254 } else if len(report.Entries) > 0 { 255 plog.Warningf("parsing Container Linux config: %s", report) 256 } 257 258 ignc, report := ct.Convert(clc, ctPlatform, ast) 259 if report.IsFatal() { 260 return nil, fmt.Errorf("rendering Container Linux config for platform %q: %s", ctPlatform, report) 261 } else if len(report.Entries) > 0 { 262 plog.Warningf("rendering Container Linux config: %s", report) 263 } 264 265 c.ignitionV22 = &ignc 266 default: 267 panic("invalid kind") 268 } 269 270 if len(u.extraKeys) > 0 { 271 // not a no-op in the zero-key case 272 c.CopyKeys(u.extraKeys) 273 } 274 275 return c, nil 276 } 277 278 // String returns the string representation of the userdata in Conf. 279 func (c *Conf) String() string { 280 if c.ignitionV1 != nil { 281 buf, _ := json.Marshal(c.ignitionV1) 282 return string(buf) 283 } else if c.ignitionV2 != nil { 284 buf, _ := json.Marshal(c.ignitionV2) 285 return string(buf) 286 } else if c.ignitionV21 != nil { 287 buf, _ := json.Marshal(c.ignitionV21) 288 return string(buf) 289 } else if c.ignitionV22 != nil { 290 buf, _ := json.Marshal(c.ignitionV22) 291 return string(buf) 292 } else if c.ignitionV23 != nil { 293 buf, _ := json.Marshal(c.ignitionV23) 294 return string(buf) 295 } else if c.ignitionV3 != nil { 296 buf, _ := json.Marshal(c.ignitionV3) 297 return string(buf) 298 } else if c.cloudconfig != nil { 299 return c.cloudconfig.String() 300 } else if c.script != "" { 301 return c.script 302 } 303 304 return "" 305 } 306 307 // MergeV3 merges a config with the ignitionV3 config via Ignition's merging function. 308 func (c *Conf) MergeV3(newConfig v3types.Config) { 309 mergeConfig := v3.Merge(*c.ignitionV3, newConfig) 310 c.ignitionV3 = &mergeConfig 311 } 312 313 func (c *Conf) ValidConfig() bool { 314 if !c.IsIgnition() { 315 return false 316 } 317 val := c.getIgnitionValidateValue() 318 if c.ignitionV3 != nil { 319 rpt := ign3validate.ValidateWithContext(c.ignitionV3, nil) 320 return !rpt.IsFatal() 321 } else { 322 rpt := ignvalidate.ValidateWithoutSource(val) 323 return !rpt.IsFatal() 324 } 325 } 326 327 func (c *Conf) getIgnitionValidateValue() reflect.Value { 328 if c.ignitionV1 != nil { 329 return reflect.ValueOf(c.ignitionV1) 330 } else if c.ignitionV2 != nil { 331 return reflect.ValueOf(c.ignitionV2) 332 } else if c.ignitionV21 != nil { 333 return reflect.ValueOf(c.ignitionV21) 334 } else if c.ignitionV22 != nil { 335 return reflect.ValueOf(c.ignitionV22) 336 } else if c.ignitionV23 != nil { 337 return reflect.ValueOf(c.ignitionV23) 338 } else if c.ignitionV3 != nil { 339 return reflect.ValueOf(c.ignitionV3) 340 } 341 return reflect.ValueOf(nil) 342 } 343 344 // WriteFile writes the userdata in Conf to a local file. 345 func (c *Conf) WriteFile(name string) error { 346 return ioutil.WriteFile(name, []byte(c.String()), 0666) 347 } 348 349 // Bytes returns the serialized userdata in Conf. 350 func (c *Conf) Bytes() []byte { 351 return []byte(c.String()) 352 } 353 354 func (c *Conf) addFileV2(path, filesystem, contents string, mode int) { 355 u, err := url.Parse(dataurl.EncodeBytes([]byte(contents))) 356 if err != nil { 357 plog.Warningf("parsing dataurl contents: %v", err) 358 return 359 } 360 c.ignitionV2.Storage.Files = append(c.ignitionV2.Storage.Files, v2types.File{ 361 Filesystem: filesystem, 362 Path: v2types.Path(path), 363 Contents: v2types.FileContents{ 364 Source: v2types.Url(*u), 365 }, 366 Mode: v2types.FileMode(os.FileMode(mode)), 367 }) 368 } 369 370 func (c *Conf) addFileV21(path, filesystem, contents string, mode int) { 371 c.ignitionV21.Storage.Files = append(c.ignitionV21.Storage.Files, v21types.File{ 372 Node: v21types.Node{ 373 Filesystem: filesystem, 374 Path: path, 375 }, 376 FileEmbedded1: v21types.FileEmbedded1{ 377 Contents: v21types.FileContents{ 378 Source: dataurl.EncodeBytes([]byte(contents)), 379 }, 380 Mode: mode, 381 }, 382 }) 383 } 384 385 func (c *Conf) addFileV22(path, filesystem, contents string, mode int) { 386 c.ignitionV22.Storage.Files = append(c.ignitionV22.Storage.Files, v22types.File{ 387 Node: v22types.Node{ 388 Filesystem: filesystem, 389 Path: path, 390 }, 391 FileEmbedded1: v22types.FileEmbedded1{ 392 Contents: v22types.FileContents{ 393 Source: dataurl.EncodeBytes([]byte(contents)), 394 }, 395 Mode: &mode, 396 }, 397 }) 398 } 399 400 func (c *Conf) addFileV23(path, filesystem, contents string, mode int) { 401 c.ignitionV23.Storage.Files = append(c.ignitionV23.Storage.Files, v23types.File{ 402 Node: v23types.Node{ 403 Filesystem: filesystem, 404 Path: path, 405 }, 406 FileEmbedded1: v23types.FileEmbedded1{ 407 Contents: v23types.FileContents{ 408 Source: dataurl.EncodeBytes([]byte(contents)), 409 }, 410 Mode: &mode, 411 }, 412 }) 413 } 414 415 func (c *Conf) addFileV3(path, filesystem, contents string, mode int) { 416 source := dataurl.EncodeBytes([]byte(contents)) 417 newConfig := v3types.Config{ 418 Ignition: v3types.Ignition{ 419 Version: "3.0.0", 420 }, 421 Storage: v3types.Storage{ 422 Files: []v3types.File{ 423 { 424 Node: v3types.Node{ 425 Path: path, 426 }, 427 FileEmbedded1: v3types.FileEmbedded1{ 428 Contents: v3types.FileContents{ 429 Source: &source, 430 }, 431 Mode: &mode, 432 }, 433 }, 434 }, 435 }, 436 } 437 c.MergeV3(newConfig) 438 } 439 440 func (c *Conf) AddFile(path, filesystem, contents string, mode int) { 441 if c.ignitionV3 != nil { 442 c.addFileV3(path, filesystem, contents, mode) 443 } else if c.ignitionV2 != nil { 444 c.addFileV2(path, filesystem, contents, mode) 445 } else if c.ignitionV21 != nil { 446 c.addFileV21(path, filesystem, contents, mode) 447 } else if c.ignitionV22 != nil { 448 c.addFileV22(path, filesystem, contents, mode) 449 } else if c.ignitionV23 != nil { 450 c.addFileV23(path, filesystem, contents, mode) 451 } else if c.ignitionV1 != nil || c.cloudconfig != nil { 452 panic("conf: AddFile does not support ignition v1 or cloudconfig") 453 } 454 } 455 456 func (c *Conf) addSystemdUnitV1(name, contents string, enable bool) { 457 c.ignitionV1.Systemd.Units = append(c.ignitionV1.Systemd.Units, v1types.SystemdUnit{ 458 Name: v1types.SystemdUnitName(name), 459 Contents: contents, 460 Enable: enable, 461 }) 462 } 463 464 func (c *Conf) addSystemdUnitV2(name, contents string, enable bool) { 465 c.ignitionV2.Systemd.Units = append(c.ignitionV2.Systemd.Units, v2types.SystemdUnit{ 466 Name: v2types.SystemdUnitName(name), 467 Contents: contents, 468 Enable: enable, 469 }) 470 } 471 472 func (c *Conf) addSystemdUnitV21(name, contents string, enable bool) { 473 c.ignitionV21.Systemd.Units = append(c.ignitionV21.Systemd.Units, v21types.Unit{ 474 Name: name, 475 Contents: contents, 476 Enabled: &enable, 477 }) 478 } 479 480 func (c *Conf) addSystemdUnitV22(name, contents string, enable bool) { 481 c.ignitionV22.Systemd.Units = append(c.ignitionV22.Systemd.Units, v22types.Unit{ 482 Name: name, 483 Contents: contents, 484 Enabled: &enable, 485 }) 486 } 487 488 func (c *Conf) addSystemdUnitV23(name, contents string, enable bool) { 489 c.ignitionV23.Systemd.Units = append(c.ignitionV23.Systemd.Units, v23types.Unit{ 490 Name: name, 491 Contents: contents, 492 Enabled: &enable, 493 }) 494 } 495 496 func (c *Conf) addSystemdUnitV3(name, contents string, enable bool) { 497 newConfig := v3types.Config{ 498 Ignition: v3types.Ignition{ 499 Version: "3.0.0", 500 }, 501 Systemd: v3types.Systemd{ 502 Units: []v3types.Unit{ 503 { 504 Name: name, 505 Contents: &contents, 506 Enabled: &enable, 507 }, 508 }, 509 }, 510 } 511 c.MergeV3(newConfig) 512 } 513 514 func (c *Conf) addSystemdUnitCloudConfig(name, contents string, enable bool) { 515 c.cloudconfig.CoreOS.Units = append(c.cloudconfig.CoreOS.Units, cci.Unit{ 516 Name: name, 517 Content: contents, 518 Enable: enable, 519 }) 520 } 521 522 func (c *Conf) AddSystemdUnit(name, contents string, enable bool) { 523 if c.ignitionV1 != nil { 524 c.addSystemdUnitV1(name, contents, enable) 525 } else if c.ignitionV2 != nil { 526 c.addSystemdUnitV2(name, contents, enable) 527 } else if c.ignitionV21 != nil { 528 c.addSystemdUnitV21(name, contents, enable) 529 } else if c.ignitionV22 != nil { 530 c.addSystemdUnitV22(name, contents, enable) 531 } else if c.ignitionV23 != nil { 532 c.addSystemdUnitV23(name, contents, enable) 533 } else if c.ignitionV3 != nil { 534 c.addSystemdUnitV3(name, contents, enable) 535 } else if c.cloudconfig != nil { 536 c.addSystemdUnitCloudConfig(name, contents, enable) 537 } 538 } 539 540 func (c *Conf) addSystemdDropinV1(service, name, contents string) { 541 for i, unit := range c.ignitionV1.Systemd.Units { 542 if unit.Name == v1types.SystemdUnitName(service) { 543 unit.DropIns = append(unit.DropIns, v1types.SystemdUnitDropIn{ 544 Name: v1types.SystemdUnitDropInName(name), 545 Contents: contents, 546 }) 547 c.ignitionV1.Systemd.Units[i] = unit 548 return 549 } 550 } 551 c.ignitionV1.Systemd.Units = append(c.ignitionV1.Systemd.Units, v1types.SystemdUnit{ 552 Name: v1types.SystemdUnitName(service), 553 DropIns: []v1types.SystemdUnitDropIn{ 554 { 555 Name: v1types.SystemdUnitDropInName(name), 556 Contents: contents, 557 }, 558 }, 559 }) 560 } 561 562 func (c *Conf) addSystemdDropinV2(service, name, contents string) { 563 for i, unit := range c.ignitionV2.Systemd.Units { 564 if unit.Name == v2types.SystemdUnitName(service) { 565 unit.DropIns = append(unit.DropIns, v2types.SystemdUnitDropIn{ 566 Name: v2types.SystemdUnitDropInName(name), 567 Contents: contents, 568 }) 569 c.ignitionV2.Systemd.Units[i] = unit 570 return 571 } 572 } 573 c.ignitionV2.Systemd.Units = append(c.ignitionV2.Systemd.Units, v2types.SystemdUnit{ 574 Name: v2types.SystemdUnitName(service), 575 DropIns: []v2types.SystemdUnitDropIn{ 576 { 577 Name: v2types.SystemdUnitDropInName(name), 578 Contents: contents, 579 }, 580 }, 581 }) 582 } 583 584 func (c *Conf) addSystemdDropinV21(service, name, contents string) { 585 for i, unit := range c.ignitionV21.Systemd.Units { 586 if unit.Name == service { 587 unit.Dropins = append(unit.Dropins, v21types.Dropin{ 588 Name: name, 589 Contents: contents, 590 }) 591 c.ignitionV21.Systemd.Units[i] = unit 592 return 593 } 594 } 595 c.ignitionV21.Systemd.Units = append(c.ignitionV21.Systemd.Units, v21types.Unit{ 596 Name: service, 597 Dropins: []v21types.Dropin{ 598 { 599 Name: name, 600 Contents: contents, 601 }, 602 }, 603 }) 604 } 605 606 func (c *Conf) addSystemdDropinV22(service, name, contents string) { 607 for i, unit := range c.ignitionV22.Systemd.Units { 608 if unit.Name == service { 609 unit.Dropins = append(unit.Dropins, v22types.SystemdDropin{ 610 Name: name, 611 Contents: contents, 612 }) 613 c.ignitionV22.Systemd.Units[i] = unit 614 return 615 } 616 } 617 c.ignitionV22.Systemd.Units = append(c.ignitionV22.Systemd.Units, v22types.Unit{ 618 Name: service, 619 Dropins: []v22types.SystemdDropin{ 620 { 621 Name: name, 622 Contents: contents, 623 }, 624 }, 625 }) 626 } 627 628 func (c *Conf) addSystemdDropinV23(service, name, contents string) { 629 for i, unit := range c.ignitionV23.Systemd.Units { 630 if unit.Name == service { 631 unit.Dropins = append(unit.Dropins, v23types.SystemdDropin{ 632 Name: name, 633 Contents: contents, 634 }) 635 c.ignitionV23.Systemd.Units[i] = unit 636 return 637 } 638 } 639 c.ignitionV23.Systemd.Units = append(c.ignitionV23.Systemd.Units, v23types.Unit{ 640 Name: service, 641 Dropins: []v23types.SystemdDropin{ 642 { 643 Name: name, 644 Contents: contents, 645 }, 646 }, 647 }) 648 } 649 650 func (c *Conf) addSystemdDropinV3(service, name, contents string) { 651 newConfig := v3types.Config{ 652 Ignition: v3types.Ignition{ 653 Version: "3.0.0", 654 }, 655 Systemd: v3types.Systemd{ 656 Units: []v3types.Unit{ 657 { 658 Name: service, 659 Dropins: []v3types.Dropin{ 660 { 661 Name: name, 662 Contents: &contents, 663 }, 664 }, 665 }, 666 }, 667 }, 668 } 669 c.MergeV3(newConfig) 670 } 671 672 func (c *Conf) addSystemdDropinCloudConfig(service, name, contents string) { 673 for i, unit := range c.cloudconfig.CoreOS.Units { 674 if unit.Name == service { 675 unit.DropIns = append(unit.DropIns, cci.UnitDropIn{ 676 Name: name, 677 Content: contents, 678 }) 679 c.cloudconfig.CoreOS.Units[i] = unit 680 return 681 } 682 } 683 c.cloudconfig.CoreOS.Units = append(c.cloudconfig.CoreOS.Units, cci.Unit{ 684 Name: service, 685 DropIns: []cci.UnitDropIn{ 686 { 687 Name: name, 688 Content: contents, 689 }, 690 }, 691 }) 692 } 693 694 func (c *Conf) AddSystemdUnitDropin(service, name, contents string) { 695 if c.ignitionV1 != nil { 696 c.addSystemdDropinV1(service, name, contents) 697 } else if c.ignitionV2 != nil { 698 c.addSystemdDropinV2(service, name, contents) 699 } else if c.ignitionV21 != nil { 700 c.addSystemdDropinV21(service, name, contents) 701 } else if c.ignitionV22 != nil { 702 c.addSystemdDropinV22(service, name, contents) 703 } else if c.ignitionV23 != nil { 704 c.addSystemdDropinV23(service, name, contents) 705 } else if c.ignitionV3 != nil { 706 c.addSystemdDropinV3(service, name, contents) 707 } else if c.cloudconfig != nil { 708 c.addSystemdDropinCloudConfig(service, name, contents) 709 } 710 } 711 712 func (c *Conf) copyKeysIgnitionV1(keys []*agent.Key) { 713 keyStrs := keysToStrings(keys) 714 for i := range c.ignitionV1.Passwd.Users { 715 user := &c.ignitionV1.Passwd.Users[i] 716 if user.Name == "core" { 717 user.SSHAuthorizedKeys = append(user.SSHAuthorizedKeys, keyStrs...) 718 return 719 } 720 } 721 c.ignitionV1.Passwd.Users = append(c.ignitionV1.Passwd.Users, v1types.User{ 722 Name: "core", 723 SSHAuthorizedKeys: keyStrs, 724 }) 725 } 726 727 func (c *Conf) copyKeysIgnitionV2(keys []*agent.Key) { 728 keyStrs := keysToStrings(keys) 729 for i := range c.ignitionV2.Passwd.Users { 730 user := &c.ignitionV2.Passwd.Users[i] 731 if user.Name == "core" { 732 user.SSHAuthorizedKeys = append(user.SSHAuthorizedKeys, keyStrs...) 733 return 734 } 735 } 736 c.ignitionV2.Passwd.Users = append(c.ignitionV2.Passwd.Users, v2types.User{ 737 Name: "core", 738 SSHAuthorizedKeys: keyStrs, 739 }) 740 } 741 742 func (c *Conf) copyKeysIgnitionV21(keys []*agent.Key) { 743 var keyObjs []v21types.SSHAuthorizedKey 744 for _, key := range keys { 745 keyObjs = append(keyObjs, v21types.SSHAuthorizedKey(key.String())) 746 } 747 for i := range c.ignitionV21.Passwd.Users { 748 user := &c.ignitionV21.Passwd.Users[i] 749 if user.Name == "core" { 750 user.SSHAuthorizedKeys = append(user.SSHAuthorizedKeys, keyObjs...) 751 return 752 } 753 } 754 c.ignitionV21.Passwd.Users = append(c.ignitionV21.Passwd.Users, v21types.PasswdUser{ 755 Name: "core", 756 SSHAuthorizedKeys: keyObjs, 757 }) 758 } 759 760 func (c *Conf) copyKeysIgnitionV22(keys []*agent.Key) { 761 var keyObjs []v22types.SSHAuthorizedKey 762 for _, key := range keys { 763 keyObjs = append(keyObjs, v22types.SSHAuthorizedKey(key.String())) 764 } 765 for i := range c.ignitionV22.Passwd.Users { 766 user := &c.ignitionV22.Passwd.Users[i] 767 if user.Name == "core" { 768 user.SSHAuthorizedKeys = append(user.SSHAuthorizedKeys, keyObjs...) 769 return 770 } 771 } 772 c.ignitionV22.Passwd.Users = append(c.ignitionV22.Passwd.Users, v22types.PasswdUser{ 773 Name: "core", 774 SSHAuthorizedKeys: keyObjs, 775 }) 776 } 777 778 func (c *Conf) copyKeysIgnitionV23(keys []*agent.Key) { 779 var keyObjs []v23types.SSHAuthorizedKey 780 for _, key := range keys { 781 keyObjs = append(keyObjs, v23types.SSHAuthorizedKey(key.String())) 782 } 783 for i := range c.ignitionV23.Passwd.Users { 784 user := &c.ignitionV23.Passwd.Users[i] 785 if user.Name == "core" { 786 user.SSHAuthorizedKeys = append(user.SSHAuthorizedKeys, keyObjs...) 787 return 788 } 789 } 790 c.ignitionV23.Passwd.Users = append(c.ignitionV23.Passwd.Users, v23types.PasswdUser{ 791 Name: "core", 792 SSHAuthorizedKeys: keyObjs, 793 }) 794 } 795 796 func (c *Conf) copyKeysIgnitionV3(keys []*agent.Key) { 797 var keyObjs []v3types.SSHAuthorizedKey 798 for _, key := range keys { 799 keyObjs = append(keyObjs, v3types.SSHAuthorizedKey(key.String())) 800 } 801 newConfig := v3types.Config{ 802 Ignition: v3types.Ignition{ 803 Version: "3.0.0", 804 }, 805 Passwd: v3types.Passwd{ 806 Users: []v3types.PasswdUser{ 807 { 808 Name: "core", 809 SSHAuthorizedKeys: keyObjs, 810 }, 811 }, 812 }, 813 } 814 c.MergeV3(newConfig) 815 } 816 817 func (c *Conf) copyKeysCloudConfig(keys []*agent.Key) { 818 c.cloudconfig.SSHAuthorizedKeys = append(c.cloudconfig.SSHAuthorizedKeys, keysToStrings(keys)...) 819 } 820 821 func (c *Conf) copyKeysScript(keys []*agent.Key) { 822 keyString := strings.Join(keysToStrings(keys), "\n") 823 c.script = strings.Replace(c.script, "@SSH_KEYS@", keyString, -1) 824 } 825 826 // CopyKeys copies public keys from agent ag into the configuration to the 827 // appropriate configuration section for the core user. 828 func (c *Conf) CopyKeys(keys []*agent.Key) { 829 if c.ignitionV1 != nil { 830 c.copyKeysIgnitionV1(keys) 831 } else if c.ignitionV2 != nil { 832 c.copyKeysIgnitionV2(keys) 833 } else if c.ignitionV21 != nil { 834 c.copyKeysIgnitionV21(keys) 835 } else if c.ignitionV22 != nil { 836 c.copyKeysIgnitionV22(keys) 837 } else if c.ignitionV23 != nil { 838 c.copyKeysIgnitionV23(keys) 839 } else if c.ignitionV3 != nil { 840 c.copyKeysIgnitionV3(keys) 841 } else if c.cloudconfig != nil { 842 c.copyKeysCloudConfig(keys) 843 } else if c.script != "" { 844 c.copyKeysScript(keys) 845 } 846 } 847 848 func keysToStrings(keys []*agent.Key) (keyStrs []string) { 849 for _, key := range keys { 850 keyStrs = append(keyStrs, key.String()) 851 } 852 return 853 } 854 855 // IsIgnition returns true if the config is for Ignition. 856 // Returns false in the case of empty configs as on most platforms, 857 // this will default back to cloudconfig 858 func (c *Conf) IsIgnition() bool { 859 return c.ignitionV1 != nil || c.ignitionV2 != nil || c.ignitionV21 != nil || c.ignitionV22 != nil || c.ignitionV23 != nil || c.ignitionV3 != nil 860 } 861 862 func (c *Conf) IsEmpty() bool { 863 return !c.IsIgnition() && c.cloudconfig == nil && c.script == "" 864 }