From 3e15d4f301ae55525697af2b414f27275be378a3 Mon Sep 17 00:00:00 2001 From: staebler Date: Tue, 26 Feb 2019 13:26:43 -0500 Subject: [PATCH 1/5] vendor: machine-config-operator for MachineConfig type Vendor openshift/machine-config-operator to have access to the MachineConfig type. The MachineConfig type is used to send supplement the ignition config for machine pools that have hyperthreading enabled. This also adds some more packages from coreos/ignition. --- Gopkg.lock | 24 +- .../coreos/ignition/config/v1/cloudinit.go | 53 +++ .../coreos/ignition/config/v1/config.go | 59 +++ .../coreos/ignition/config/v1/types/config.go | 27 ++ .../coreos/ignition/config/v1/types/disk.go | 123 ++++++ .../coreos/ignition/config/v1/types/file.go | 39 ++ .../ignition/config/v1/types/filesystem.go | 45 +++ .../coreos/ignition/config/v1/types/group.go | 22 ++ .../ignition/config/v1/types/networkd.go | 19 + .../ignition/config/v1/types/partition.go | 60 +++ .../coreos/ignition/config/v1/types/passwd.go | 20 + .../coreos/ignition/config/v1/types/path.go | 31 ++ .../coreos/ignition/config/v1/types/raid.go | 44 +++ .../ignition/config/v1/types/storage.go | 21 ++ .../ignition/config/v1/types/systemd.go | 19 + .../coreos/ignition/config/v1/types/unit.go | 73 ++++ .../coreos/ignition/config/v1/types/user.go | 35 ++ .../coreos/ignition/config/v2_0/append.go | 73 ++++ .../coreos/ignition/config/v2_0/cloudinit.go | 53 +++ .../coreos/ignition/config/v2_0/config.go | 70 ++++ .../coreos/ignition/config/v2_0/translate.go | 173 +++++++++ .../ignition/config/v2_0/types/compression.go | 31 ++ .../ignition/config/v2_0/types/config.go | 87 +++++ .../coreos/ignition/config/v2_0/types/disk.go | 126 +++++++ .../coreos/ignition/config/v2_0/types/file.go | 61 +++ .../ignition/config/v2_0/types/filesystem.go | 60 +++ .../ignition/config/v2_0/types/group.go | 22 ++ .../coreos/ignition/config/v2_0/types/hash.go | 72 ++++ .../ignition/config/v2_0/types/ignition.go | 64 ++++ .../ignition/config/v2_0/types/networkd.go | 19 + .../ignition/config/v2_0/types/partition.go | 64 ++++ .../ignition/config/v2_0/types/passwd.go | 20 + .../coreos/ignition/config/v2_0/types/path.go | 35 ++ .../coreos/ignition/config/v2_0/types/raid.go | 44 +++ .../ignition/config/v2_0/types/storage.go | 22 ++ .../ignition/config/v2_0/types/systemd.go | 19 + .../coreos/ignition/config/v2_0/types/unit.go | 115 ++++++ .../coreos/ignition/config/v2_0/types/url.go | 69 ++++ .../coreos/ignition/config/v2_0/types/user.go | 35 ++ .../config/v2_0/types/verification.go | 19 + .../coreos/ignition/config/v2_1/append.go | 72 ++++ .../coreos/ignition/config/v2_1/cloudinit.go | 53 +++ .../coreos/ignition/config/v2_1/config.go | 68 ++++ .../coreos/ignition/config/v2_1/translate.go | 236 ++++++++++++ .../ignition/config/v2_1/types/config.go | 91 +++++ .../ignition/config/v2_1/types/directory.go | 30 ++ .../coreos/ignition/config/v2_1/types/disk.go | 128 +++++++ .../coreos/ignition/config/v2_1/types/file.go | 56 +++ .../ignition/config/v2_1/types/filesystem.go | 144 +++++++ .../ignition/config/v2_1/types/ignition.go | 52 +++ .../coreos/ignition/config/v2_1/types/link.go | 33 ++ .../coreos/ignition/config/v2_1/types/mode.go | 26 ++ .../coreos/ignition/config/v2_1/types/node.go | 73 ++++ .../ignition/config/v2_1/types/partition.go | 77 ++++ .../ignition/config/v2_1/types/passwd.go | 67 ++++ .../coreos/ignition/config/v2_1/types/path.go | 28 ++ .../coreos/ignition/config/v2_1/types/raid.go | 57 +++ .../ignition/config/v2_1/types/schema.go | 221 +++++++++++ .../coreos/ignition/config/v2_1/types/unit.go | 109 ++++++ .../coreos/ignition/config/v2_1/types/url.go | 45 +++ .../config/v2_1/types/verification.go | 77 ++++ .../coreos/ignition/config/v2_2/append.go | 76 ++++ .../coreos/ignition/config/v2_2/cloudinit.go | 53 +++ .../coreos/ignition/config/v2_2/config.go | 71 ++++ .../coreos/ignition/config/v2_2/translate.go | 354 ++++++++++++++++++ .../ignition/config/validate/astjson/node.go | 73 ++++ .../config/validate/astnode/astnode.go | 45 +++ .../ignition/config/validate/validate.go | 242 ++++++++++++ .../openshift/machine-config-operator/LICENSE | 191 ++++++++++ .../machine-config-operator/pkg/apis/apis.go | 4 + .../v1/doc.go | 4 + .../v1/helpers.go | 87 +++++ .../v1/machineconfig.deepcopy.go | 57 +++ .../v1/register.go | 53 +++ .../v1/types.go | 337 +++++++++++++++++ .../v1/zz_generated.deepcopy.go | 338 +++++++++++++++++ 76 files changed, 5794 insertions(+), 1 deletion(-) create mode 100644 vendor/github.com/coreos/ignition/config/v1/cloudinit.go create mode 100644 vendor/github.com/coreos/ignition/config/v1/config.go create mode 100644 vendor/github.com/coreos/ignition/config/v1/types/config.go create mode 100644 vendor/github.com/coreos/ignition/config/v1/types/disk.go create mode 100644 vendor/github.com/coreos/ignition/config/v1/types/file.go create mode 100644 vendor/github.com/coreos/ignition/config/v1/types/filesystem.go create mode 100644 vendor/github.com/coreos/ignition/config/v1/types/group.go create mode 100644 vendor/github.com/coreos/ignition/config/v1/types/networkd.go create mode 100644 vendor/github.com/coreos/ignition/config/v1/types/partition.go create mode 100644 vendor/github.com/coreos/ignition/config/v1/types/passwd.go create mode 100644 vendor/github.com/coreos/ignition/config/v1/types/path.go create mode 100644 vendor/github.com/coreos/ignition/config/v1/types/raid.go create mode 100644 vendor/github.com/coreos/ignition/config/v1/types/storage.go create mode 100644 vendor/github.com/coreos/ignition/config/v1/types/systemd.go create mode 100644 vendor/github.com/coreos/ignition/config/v1/types/unit.go create mode 100644 vendor/github.com/coreos/ignition/config/v1/types/user.go create mode 100644 vendor/github.com/coreos/ignition/config/v2_0/append.go create mode 100644 vendor/github.com/coreos/ignition/config/v2_0/cloudinit.go create mode 100644 vendor/github.com/coreos/ignition/config/v2_0/config.go create mode 100644 vendor/github.com/coreos/ignition/config/v2_0/translate.go create mode 100644 vendor/github.com/coreos/ignition/config/v2_0/types/compression.go create mode 100644 vendor/github.com/coreos/ignition/config/v2_0/types/config.go create mode 100644 vendor/github.com/coreos/ignition/config/v2_0/types/disk.go create mode 100644 vendor/github.com/coreos/ignition/config/v2_0/types/file.go create mode 100644 vendor/github.com/coreos/ignition/config/v2_0/types/filesystem.go create mode 100644 vendor/github.com/coreos/ignition/config/v2_0/types/group.go create mode 100644 vendor/github.com/coreos/ignition/config/v2_0/types/hash.go create mode 100644 vendor/github.com/coreos/ignition/config/v2_0/types/ignition.go create mode 100644 vendor/github.com/coreos/ignition/config/v2_0/types/networkd.go create mode 100644 vendor/github.com/coreos/ignition/config/v2_0/types/partition.go create mode 100644 vendor/github.com/coreos/ignition/config/v2_0/types/passwd.go create mode 100644 vendor/github.com/coreos/ignition/config/v2_0/types/path.go create mode 100644 vendor/github.com/coreos/ignition/config/v2_0/types/raid.go create mode 100644 vendor/github.com/coreos/ignition/config/v2_0/types/storage.go create mode 100644 vendor/github.com/coreos/ignition/config/v2_0/types/systemd.go create mode 100644 vendor/github.com/coreos/ignition/config/v2_0/types/unit.go create mode 100644 vendor/github.com/coreos/ignition/config/v2_0/types/url.go create mode 100644 vendor/github.com/coreos/ignition/config/v2_0/types/user.go create mode 100644 vendor/github.com/coreos/ignition/config/v2_0/types/verification.go create mode 100644 vendor/github.com/coreos/ignition/config/v2_1/append.go create mode 100644 vendor/github.com/coreos/ignition/config/v2_1/cloudinit.go create mode 100644 vendor/github.com/coreos/ignition/config/v2_1/config.go create mode 100644 vendor/github.com/coreos/ignition/config/v2_1/translate.go create mode 100644 vendor/github.com/coreos/ignition/config/v2_1/types/config.go create mode 100644 vendor/github.com/coreos/ignition/config/v2_1/types/directory.go create mode 100644 vendor/github.com/coreos/ignition/config/v2_1/types/disk.go create mode 100644 vendor/github.com/coreos/ignition/config/v2_1/types/file.go create mode 100644 vendor/github.com/coreos/ignition/config/v2_1/types/filesystem.go create mode 100644 vendor/github.com/coreos/ignition/config/v2_1/types/ignition.go create mode 100644 vendor/github.com/coreos/ignition/config/v2_1/types/link.go create mode 100644 vendor/github.com/coreos/ignition/config/v2_1/types/mode.go create mode 100644 vendor/github.com/coreos/ignition/config/v2_1/types/node.go create mode 100644 vendor/github.com/coreos/ignition/config/v2_1/types/partition.go create mode 100644 vendor/github.com/coreos/ignition/config/v2_1/types/passwd.go create mode 100644 vendor/github.com/coreos/ignition/config/v2_1/types/path.go create mode 100644 vendor/github.com/coreos/ignition/config/v2_1/types/raid.go create mode 100644 vendor/github.com/coreos/ignition/config/v2_1/types/schema.go create mode 100644 vendor/github.com/coreos/ignition/config/v2_1/types/unit.go create mode 100644 vendor/github.com/coreos/ignition/config/v2_1/types/url.go create mode 100644 vendor/github.com/coreos/ignition/config/v2_1/types/verification.go create mode 100644 vendor/github.com/coreos/ignition/config/v2_2/append.go create mode 100644 vendor/github.com/coreos/ignition/config/v2_2/cloudinit.go create mode 100644 vendor/github.com/coreos/ignition/config/v2_2/config.go create mode 100644 vendor/github.com/coreos/ignition/config/v2_2/translate.go create mode 100644 vendor/github.com/coreos/ignition/config/validate/astjson/node.go create mode 100644 vendor/github.com/coreos/ignition/config/validate/astnode/astnode.go create mode 100644 vendor/github.com/coreos/ignition/config/validate/validate.go create mode 100644 vendor/github.com/openshift/machine-config-operator/LICENSE create mode 100644 vendor/github.com/openshift/machine-config-operator/pkg/apis/apis.go create mode 100644 vendor/github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1/doc.go create mode 100644 vendor/github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1/helpers.go create mode 100644 vendor/github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1/machineconfig.deepcopy.go create mode 100644 vendor/github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1/register.go create mode 100644 vendor/github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1/types.go create mode 100644 vendor/github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1/zz_generated.deepcopy.go diff --git a/Gopkg.lock b/Gopkg.lock index 270bc4fdf9d..26f983a9c8b 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -106,14 +106,24 @@ version = "v18" [[projects]] - digest = "1:1a216755b1570c329bada24deac0032f487c1a5b7c690963c96ad2da9b8c3ec9" + digest = "1:fb5b2aed0d774e210c1f2c0893e620cb3fa98957d3b925d0378f93b9613eb605" name = "github.com/coreos/ignition" packages = [ "config/shared/errors", "config/shared/validations", "config/util", + "config/v1", + "config/v1/types", + "config/v2_0", + "config/v2_0/types", + "config/v2_1", + "config/v2_1/types", + "config/v2_2", "config/v2_2/types", "config/v2_3_experimental/types", + "config/validate", + "config/validate/astjson", + "config/validate/astnode", "config/validate/report", ] pruneopts = "NUT" @@ -441,6 +451,17 @@ pruneopts = "NUT" revision = "4b9f6ceb6598ebd4dd6c9385ef024abf7a18159b" +[[projects]] + digest = "1:5bfff65eba275066cdf304a0faf91c7709e0f612ebb9760046adf6035d63ef5c" + name = "github.com/openshift/machine-config-operator" + packages = [ + "pkg/apis", + "pkg/apis/machineconfiguration.openshift.io/v1", + ] + pruneopts = "NUT" + revision = "7efcbbbcada2af39a505c7a0305c7b32acec0af3" + version = "v3.11.0" + [[projects]] digest = "1:93b1d84c5fa6d1ea52f4114c37714cddd84d5b78f151b62bb101128dd51399bf" name = "github.com/pborman/uuid" @@ -966,6 +987,7 @@ "github.com/openshift/cluster-api-provider-libvirt/pkg/apis/libvirtproviderconfig/v1alpha1", "github.com/openshift/cluster-api/pkg/apis/machine/v1beta1", "github.com/openshift/library-go/pkg/config/clusteroperator/v1helpers", + "github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1", "github.com/pborman/uuid", "github.com/peterbourgon/diskv", "github.com/pkg/errors", diff --git a/vendor/github.com/coreos/ignition/config/v1/cloudinit.go b/vendor/github.com/coreos/ignition/config/v1/cloudinit.go new file mode 100644 index 00000000000..7cfeb455938 --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v1/cloudinit.go @@ -0,0 +1,53 @@ +// Copyright 2015 CoreOS, Inc. +// +// 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. + +// These functions are copied from github.com/coreos/coreos-cloudinit/config. + +package v1 + +import ( + "bytes" + "compress/gzip" + "io/ioutil" + "strings" + "unicode" +) + +func isCloudConfig(userdata []byte) bool { + header := strings.SplitN(string(decompressIfGzipped(userdata)), "\n", 2)[0] + + // Trim trailing whitespaces + header = strings.TrimRightFunc(header, unicode.IsSpace) + + return (header == "#cloud-config") +} + +func isScript(userdata []byte) bool { + header := strings.SplitN(string(decompressIfGzipped(userdata)), "\n", 2)[0] + return strings.HasPrefix(header, "#!") +} + +func decompressIfGzipped(data []byte) []byte { + if reader, err := gzip.NewReader(bytes.NewReader(data)); err == nil { + uncompressedData, err := ioutil.ReadAll(reader) + reader.Close() + if err == nil { + return uncompressedData + } else { + return data + } + } else { + return data + } +} diff --git a/vendor/github.com/coreos/ignition/config/v1/config.go b/vendor/github.com/coreos/ignition/config/v1/config.go new file mode 100644 index 00000000000..21e79f81e54 --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v1/config.go @@ -0,0 +1,59 @@ +// Copyright 2015 CoreOS, Inc. +// +// 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 v1 + +import ( + "github.com/coreos/ignition/config/shared/errors" + "github.com/coreos/ignition/config/util" + "github.com/coreos/ignition/config/v1/types" + "github.com/coreos/ignition/config/validate" + "github.com/coreos/ignition/config/validate/report" + + json "github.com/ajeddeloh/go-json" +) + +func Parse(rawConfig []byte) (types.Config, report.Report, error) { + if isEmpty(rawConfig) { + return types.Config{}, report.Report{}, errors.ErrEmpty + } else if isCloudConfig(rawConfig) { + return types.Config{}, report.Report{}, errors.ErrCloudConfig + } else if isScript(rawConfig) { + return types.Config{}, report.Report{}, errors.ErrScript + } + + var err error + var config types.Config + + err = json.Unmarshal(rawConfig, &config) + if err != nil { + rpt, err := util.HandleParseErrors(rawConfig) + // HandleParseErrors always returns an error + return types.Config{}, rpt, err + } + + if config.Version != types.Version { + return types.Config{}, report.Report{}, errors.ErrUnknownVersion + } + + rpt := validate.ValidateConfig(rawConfig, config) + if rpt.IsFatal() { + return types.Config{}, rpt, errors.ErrInvalid + } + return config, rpt, nil +} + +func isEmpty(userdata []byte) bool { + return len(userdata) == 0 +} diff --git a/vendor/github.com/coreos/ignition/config/v1/types/config.go b/vendor/github.com/coreos/ignition/config/v1/types/config.go new file mode 100644 index 00000000000..f9215699cbd --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v1/types/config.go @@ -0,0 +1,27 @@ +// Copyright 2016 CoreOS, Inc. +// +// 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 types + +const ( + Version = 1 +) + +type Config struct { + Version int `json:"ignitionVersion"` + Storage Storage `json:"storage,omitempty"` + Systemd Systemd `json:"systemd,omitempty"` + Networkd Networkd `json:"networkd,omitempty"` + Passwd Passwd `json:"passwd,omitempty"` +} diff --git a/vendor/github.com/coreos/ignition/config/v1/types/disk.go b/vendor/github.com/coreos/ignition/config/v1/types/disk.go new file mode 100644 index 00000000000..62517856dc0 --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v1/types/disk.go @@ -0,0 +1,123 @@ +// Copyright 2016 CoreOS, Inc. +// +// 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 types + +import ( + "github.com/coreos/ignition/config/shared/errors" + "github.com/coreos/ignition/config/validate/report" +) + +type Disk struct { + Device Path `json:"device,omitempty"` + WipeTable bool `json:"wipeTable,omitempty"` + Partitions []Partition `json:"partitions,omitempty"` +} + +func (n Disk) Validate() report.Report { + r := report.Report{} + if len(n.Device) == 0 { + r.Add(report.Entry{ + Kind: report.EntryError, + Message: errors.ErrDiskDeviceRequired.Error(), + }) + } + if n.partitionNumbersCollide() { + r.Add(report.Entry{ + Kind: report.EntryError, + Message: errors.ErrPartitionNumbersCollide.Error(), + }) + } + if n.partitionsOverlap() { + r.Add(report.Entry{ + Kind: report.EntryError, + Message: errors.ErrPartitionsOverlap.Error(), + }) + } + if n.partitionsMisaligned() { + r.Add(report.Entry{ + Kind: report.EntryError, + Message: errors.ErrPartitionsMisaligned.Error(), + }) + } + // Disks which get to this point will likely succeed in sgdisk + return r +} + +// partitionNumbersCollide returns true if partition numbers in n.Partitions are not unique. +func (n Disk) partitionNumbersCollide() bool { + m := map[int][]Partition{} + for _, p := range n.Partitions { + m[p.Number] = append(m[p.Number], p) + } + for _, n := range m { + if len(n) > 1 { + // TODO(vc): return information describing the collision for logging + return true + } + } + return false +} + +// end returns the last sector of a partition. +func (p Partition) end() PartitionDimension { + if p.Size == 0 { + // a size of 0 means "fill available", just return the start as the end for those. + return p.Start + } + return p.Start + p.Size - 1 +} + +// partitionsOverlap returns true if any explicitly dimensioned partitions overlap +func (n Disk) partitionsOverlap() bool { + for _, p := range n.Partitions { + // Starts of 0 are placed by sgdisk into the "largest available block" at that time. + // We aren't going to check those for overlap since we don't have the disk geometry. + if p.Start == 0 { + continue + } + + for _, o := range n.Partitions { + if p == o || o.Start == 0 { + continue + } + + // is p.Start within o? + if p.Start >= o.Start && p.Start <= o.end() { + return true + } + + // is p.end() within o? + if p.end() >= o.Start && p.end() <= o.end() { + return true + } + + // do p.Start and p.end() straddle o? + if p.Start < o.Start && p.end() > o.end() { + return true + } + } + } + return false +} + +// partitionsMisaligned returns true if any of the partitions don't start on a 2048-sector (1MiB) boundary. +func (n Disk) partitionsMisaligned() bool { + for _, p := range n.Partitions { + if (p.Start & (2048 - 1)) != 0 { + return true + } + } + return false +} diff --git a/vendor/github.com/coreos/ignition/config/v1/types/file.go b/vendor/github.com/coreos/ignition/config/v1/types/file.go new file mode 100644 index 00000000000..8775c19fd5e --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v1/types/file.go @@ -0,0 +1,39 @@ +// Copyright 2016 CoreOS, Inc. +// +// 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 types + +import ( + "os" + + "github.com/coreos/ignition/config/shared/errors" + "github.com/coreos/ignition/config/validate/report" +) + +type FileMode os.FileMode + +type File struct { + Path Path `json:"path,omitempty"` + Contents string `json:"contents,omitempty"` + Mode FileMode `json:"mode,omitempty"` + Uid int `json:"uid,omitempty"` + Gid int `json:"gid,omitempty"` +} + +func (m FileMode) Validate() report.Report { + if (m &^ 07777) != 0 { + return report.ReportFromError(errors.ErrFileIllegalMode, report.EntryError) + } + return report.Report{} +} diff --git a/vendor/github.com/coreos/ignition/config/v1/types/filesystem.go b/vendor/github.com/coreos/ignition/config/v1/types/filesystem.go new file mode 100644 index 00000000000..7986bd724cc --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v1/types/filesystem.go @@ -0,0 +1,45 @@ +// Copyright 2016 CoreOS, Inc. +// +// 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 types + +import ( + "github.com/coreos/ignition/config/shared/errors" + "github.com/coreos/ignition/config/validate/report" +) + +type Filesystem struct { + Device Path `json:"device,omitempty"` + Format FilesystemFormat `json:"format,omitempty"` + Create *FilesystemCreate `json:"create,omitempty"` + Files []File `json:"files,omitempty"` +} + +type FilesystemCreate struct { + Force bool `json:"force,omitempty"` + Options MkfsOptions `json:"options,omitempty"` +} + +type FilesystemFormat string + +func (f FilesystemFormat) Validate() report.Report { + switch f { + case "ext4", "btrfs", "xfs": + return report.Report{} + default: + return report.ReportFromError(errors.ErrFilesystemInvalidFormat, report.EntryError) + } +} + +type MkfsOptions []string diff --git a/vendor/github.com/coreos/ignition/config/v1/types/group.go b/vendor/github.com/coreos/ignition/config/v1/types/group.go new file mode 100644 index 00000000000..27e51048870 --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v1/types/group.go @@ -0,0 +1,22 @@ +// Copyright 2016 CoreOS, Inc. +// +// 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 types + +type Group struct { + Name string `json:"name,omitempty"` + Gid *uint `json:"gid,omitempty"` + PasswordHash string `json:"passwordHash,omitempty"` + System bool `json:"system,omitempty"` +} diff --git a/vendor/github.com/coreos/ignition/config/v1/types/networkd.go b/vendor/github.com/coreos/ignition/config/v1/types/networkd.go new file mode 100644 index 00000000000..470c721106a --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v1/types/networkd.go @@ -0,0 +1,19 @@ +// Copyright 2016 CoreOS, Inc. +// +// 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 types + +type Networkd struct { + Units []NetworkdUnit `json:"units,omitempty"` +} diff --git a/vendor/github.com/coreos/ignition/config/v1/types/partition.go b/vendor/github.com/coreos/ignition/config/v1/types/partition.go new file mode 100644 index 00000000000..16270de2cf8 --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v1/types/partition.go @@ -0,0 +1,60 @@ +// Copyright 2016 CoreOS, Inc. +// +// 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 types + +import ( + "fmt" + "regexp" + + "github.com/coreos/ignition/config/shared/errors" + "github.com/coreos/ignition/config/validate/report" +) + +type Partition struct { + Label PartitionLabel `json:"label,omitempty"` + Number int `json:"number"` + Size PartitionDimension `json:"size"` + Start PartitionDimension `json:"start"` + TypeGUID PartitionTypeGUID `json:"typeGuid,omitempty"` +} + +type PartitionLabel string + +func (n PartitionLabel) Validate() report.Report { + // http://en.wikipedia.org/wiki/GUID_Partition_Table#Partition_entries: + // 56 (0x38) 72 bytes Partition name (36 UTF-16LE code units) + + // XXX(vc): note GPT calls it a name, we're using label for consistency + // with udev naming /dev/disk/by-partlabel/*. + if len(string(n)) > 36 { + return report.ReportFromError(errors.ErrLabelTooLong, report.EntryError) + } + return report.Report{} +} + +type PartitionDimension uint64 + +type PartitionTypeGUID string + +func (d PartitionTypeGUID) Validate() report.Report { + ok, err := regexp.MatchString("^(|[[:xdigit:]]{8}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{12})$", string(d)) + if err != nil { + return report.ReportFromError(fmt.Errorf("error matching type-guid regexp: %v", err), report.EntryError) + } + if !ok { + return report.ReportFromError(fmt.Errorf(`partition type-guid must have the form "01234567-89AB-CDEF-EDCB-A98765432101", got: %q`, string(d)), report.EntryError) + } + return report.Report{} +} diff --git a/vendor/github.com/coreos/ignition/config/v1/types/passwd.go b/vendor/github.com/coreos/ignition/config/v1/types/passwd.go new file mode 100644 index 00000000000..0ffff43bb84 --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v1/types/passwd.go @@ -0,0 +1,20 @@ +// Copyright 2016 CoreOS, Inc. +// +// 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 types + +type Passwd struct { + Users []User `json:"users,omitempty"` + Groups []Group `json:"groups,omitempty"` +} diff --git a/vendor/github.com/coreos/ignition/config/v1/types/path.go b/vendor/github.com/coreos/ignition/config/v1/types/path.go new file mode 100644 index 00000000000..e37341c1ace --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v1/types/path.go @@ -0,0 +1,31 @@ +// Copyright 2016 CoreOS, Inc. +// +// 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 types + +import ( + "path" + + "github.com/coreos/ignition/config/shared/errors" + "github.com/coreos/ignition/config/validate/report" +) + +type Path string + +func (d Path) Validate() report.Report { + if !path.IsAbs(string(d)) { + return report.ReportFromError(errors.ErrPathRelative, report.EntryError) + } + return report.Report{} +} diff --git a/vendor/github.com/coreos/ignition/config/v1/types/raid.go b/vendor/github.com/coreos/ignition/config/v1/types/raid.go new file mode 100644 index 00000000000..329b123e6d0 --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v1/types/raid.go @@ -0,0 +1,44 @@ +// Copyright 2016 CoreOS, Inc. +// +// 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 types + +import ( + "github.com/coreos/ignition/config/shared/errors" + "github.com/coreos/ignition/config/validate/report" +) + +type Raid struct { + Name string `json:"name"` + Level string `json:"level"` + Devices []Path `json:"devices,omitempty"` + Spares int `json:"spares,omitempty"` +} + +func (n Raid) Validate() report.Report { + switch n.Level { + case "linear", "raid0", "0", "stripe": + if n.Spares != 0 { + return report.ReportFromError(errors.ErrSparesUnsupportedForLevel, report.EntryError) + } + case "raid1", "1", "mirror": + case "raid4", "4": + case "raid5", "5": + case "raid6", "6": + case "raid10", "10": + default: + return report.ReportFromError(errors.ErrUnrecognizedRaidLevel, report.EntryError) + } + return report.Report{} +} diff --git a/vendor/github.com/coreos/ignition/config/v1/types/storage.go b/vendor/github.com/coreos/ignition/config/v1/types/storage.go new file mode 100644 index 00000000000..2649751a7d0 --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v1/types/storage.go @@ -0,0 +1,21 @@ +// Copyright 2016 CoreOS, Inc. +// +// 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 types + +type Storage struct { + Disks []Disk `json:"disks,omitempty"` + Arrays []Raid `json:"raid,omitempty"` + Filesystems []Filesystem `json:"filesystems,omitempty"` +} diff --git a/vendor/github.com/coreos/ignition/config/v1/types/systemd.go b/vendor/github.com/coreos/ignition/config/v1/types/systemd.go new file mode 100644 index 00000000000..97194b91150 --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v1/types/systemd.go @@ -0,0 +1,19 @@ +// Copyright 2016 CoreOS, Inc. +// +// 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 types + +type Systemd struct { + Units []SystemdUnit `json:"units,omitempty"` +} diff --git a/vendor/github.com/coreos/ignition/config/v1/types/unit.go b/vendor/github.com/coreos/ignition/config/v1/types/unit.go new file mode 100644 index 00000000000..5e983cc1456 --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v1/types/unit.go @@ -0,0 +1,73 @@ +// Copyright 2016 CoreOS, Inc. +// +// 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 types + +import ( + "path" + + "github.com/coreos/ignition/config/shared/errors" + "github.com/coreos/ignition/config/validate/report" +) + +type SystemdUnit struct { + Name SystemdUnitName `json:"name,omitempty"` + Enable bool `json:"enable,omitempty"` + Mask bool `json:"mask,omitempty"` + Contents string `json:"contents,omitempty"` + DropIns []SystemdUnitDropIn `json:"dropins,omitempty"` +} + +type SystemdUnitDropIn struct { + Name SystemdUnitDropInName `json:"name,omitempty"` + Contents string `json:"contents,omitempty"` +} + +type SystemdUnitName string + +func (n SystemdUnitName) Validate() report.Report { + switch path.Ext(string(n)) { + case ".service", ".socket", ".device", ".mount", ".automount", ".swap", ".target", ".path", ".timer", ".snapshot", ".slice", ".scope": + return report.Report{} + default: + return report.ReportFromError(errors.ErrInvalidSystemdExt, report.EntryError) + } +} + +type SystemdUnitDropInName string + +func (n SystemdUnitDropInName) Validate() report.Report { + switch path.Ext(string(n)) { + case ".conf": + return report.Report{} + default: + return report.ReportFromError(errors.ErrInvalidSystemdDropinExt, report.EntryError) + } +} + +type NetworkdUnit struct { + Name NetworkdUnitName `json:"name,omitempty"` + Contents string `json:"contents,omitempty"` +} + +type NetworkdUnitName string + +func (n NetworkdUnitName) Validate() report.Report { + switch path.Ext(string(n)) { + case ".link", ".netdev", ".network": + return report.Report{} + default: + return report.ReportFromError(errors.ErrInvalidNetworkdExt, report.EntryError) + } +} diff --git a/vendor/github.com/coreos/ignition/config/v1/types/user.go b/vendor/github.com/coreos/ignition/config/v1/types/user.go new file mode 100644 index 00000000000..f6653e27494 --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v1/types/user.go @@ -0,0 +1,35 @@ +// Copyright 2016 CoreOS, Inc. +// +// 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 types + +type User struct { + Name string `json:"name,omitempty"` + PasswordHash string `json:"passwordHash,omitempty"` + SSHAuthorizedKeys []string `json:"sshAuthorizedKeys,omitempty"` + Create *UserCreate `json:"create,omitempty"` +} + +type UserCreate struct { + Uid *uint `json:"uid,omitempty"` + GECOS string `json:"gecos,omitempty"` + Homedir string `json:"homeDir,omitempty"` + NoCreateHome bool `json:"noCreateHome,omitempty"` + PrimaryGroup string `json:"primaryGroup,omitempty"` + Groups []string `json:"groups,omitempty"` + NoUserGroup bool `json:"noUserGroup,omitempty"` + System bool `json:"system,omitempty"` + NoLogInit bool `json:"noLogInit,omitempty"` + Shell string `json:"shell,omitempty"` +} diff --git a/vendor/github.com/coreos/ignition/config/v2_0/append.go b/vendor/github.com/coreos/ignition/config/v2_0/append.go new file mode 100644 index 00000000000..cee6bc412e2 --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v2_0/append.go @@ -0,0 +1,73 @@ +// Copyright 2016 CoreOS, Inc. +// +// 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 v2_0 + +import ( + "reflect" + + "github.com/coreos/ignition/config/v2_0/types" +) + +// Append appends newConfig to oldConfig and returns the result. Appending one +// config to another is accomplished by iterating over every field in the +// config structure, appending slices, recursively appending structs, and +// overwriting old values with new values for all other types. +func Append(oldConfig, newConfig types.Config) types.Config { + vOld := reflect.ValueOf(oldConfig) + vNew := reflect.ValueOf(newConfig) + + vResult := appendStruct(vOld, vNew) + + return vResult.Interface().(types.Config) +} + +// appendStruct is an internal helper function to AppendConfig. Given two values +// of structures (assumed to be the same type), recursively iterate over every +// field in the struct, appending slices, recursively appending structs, and +// overwriting old values with the new for all other types. Individual fields +// are able to override their merge strategy using the "merge" tag. Accepted +// values are "new" or "old": "new" uses the new value, "old" uses the old +// value. These are currently only used for "ignition.config" and +// "ignition.version". +func appendStruct(vOld, vNew reflect.Value) reflect.Value { + tOld := vOld.Type() + vRes := reflect.New(tOld) + + for i := 0; i < tOld.NumField(); i++ { + vfOld := vOld.Field(i) + vfNew := vNew.Field(i) + vfRes := vRes.Elem().Field(i) + + switch tOld.Field(i).Tag.Get("merge") { + case "old": + vfRes.Set(vfOld) + continue + case "new": + vfRes.Set(vfNew) + continue + } + + switch vfOld.Type().Kind() { + case reflect.Struct: + vfRes.Set(appendStruct(vfOld, vfNew)) + case reflect.Slice: + vfRes.Set(reflect.AppendSlice(vfOld, vfNew)) + default: + vfRes.Set(vfNew) + } + } + + return vRes.Elem() +} diff --git a/vendor/github.com/coreos/ignition/config/v2_0/cloudinit.go b/vendor/github.com/coreos/ignition/config/v2_0/cloudinit.go new file mode 100644 index 00000000000..9e1f2ad0e7c --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v2_0/cloudinit.go @@ -0,0 +1,53 @@ +// Copyright 2015 CoreOS, Inc. +// +// 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. + +// These functions are copied from github.com/coreos/coreos-cloudinit/config. + +package v2_0 + +import ( + "bytes" + "compress/gzip" + "io/ioutil" + "strings" + "unicode" +) + +func isCloudConfig(userdata []byte) bool { + header := strings.SplitN(string(decompressIfGzipped(userdata)), "\n", 2)[0] + + // Trim trailing whitespaces + header = strings.TrimRightFunc(header, unicode.IsSpace) + + return (header == "#cloud-config") +} + +func isScript(userdata []byte) bool { + header := strings.SplitN(string(decompressIfGzipped(userdata)), "\n", 2)[0] + return strings.HasPrefix(header, "#!") +} + +func decompressIfGzipped(data []byte) []byte { + if reader, err := gzip.NewReader(bytes.NewReader(data)); err == nil { + uncompressedData, err := ioutil.ReadAll(reader) + reader.Close() + if err == nil { + return uncompressedData + } else { + return data + } + } else { + return data + } +} diff --git a/vendor/github.com/coreos/ignition/config/v2_0/config.go b/vendor/github.com/coreos/ignition/config/v2_0/config.go new file mode 100644 index 00000000000..f1385bf1ca9 --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v2_0/config.go @@ -0,0 +1,70 @@ +// Copyright 2015 CoreOS, Inc. +// +// 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 v2_0 + +import ( + "github.com/coreos/ignition/config/shared/errors" + "github.com/coreos/ignition/config/v1" + "github.com/coreos/ignition/config/v2_0/types" + "github.com/coreos/ignition/config/validate" + "github.com/coreos/ignition/config/validate/report" + + json "github.com/ajeddeloh/go-json" + "github.com/coreos/go-semver/semver" +) + +// Parse parses the raw config into a types.Config struct and generates a report of any +// errors, warnings, info, and deprecations it encountered +func Parse(rawConfig []byte) (types.Config, report.Report, error) { + if isEmpty(rawConfig) { + return types.Config{}, report.Report{}, errors.ErrEmpty + } else if isCloudConfig(rawConfig) { + return types.Config{}, report.Report{}, errors.ErrCloudConfig + } else if isScript(rawConfig) { + return types.Config{}, report.Report{}, errors.ErrScript + } + + var err error + var config types.Config + + err = json.Unmarshal(rawConfig, &config) + + if err != nil || semver.Version(config.Ignition.Version).LessThan(types.MaxVersion) { + // We can fail unmarshaling if it's an older config. Attempt to parse + // it as such. + config, rpt, err := v1.Parse(rawConfig) + if err != nil { + return types.Config{}, rpt, err + } + + rpt.Merge(report.ReportFromError(errors.ErrDeprecated, report.EntryDeprecated)) + return TranslateFromV1(config), rpt, err + } + + if semver.Version(config.Ignition.Version) != types.MaxVersion { + return types.Config{}, report.Report{}, errors.ErrUnknownVersion + } + + rpt := validate.ValidateConfig(rawConfig, config) + if rpt.IsFatal() { + return types.Config{}, rpt, errors.ErrInvalid + } + + return config, rpt, nil +} + +func isEmpty(userdata []byte) bool { + return len(userdata) == 0 +} diff --git a/vendor/github.com/coreos/ignition/config/v2_0/translate.go b/vendor/github.com/coreos/ignition/config/v2_0/translate.go new file mode 100644 index 00000000000..832adce566d --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v2_0/translate.go @@ -0,0 +1,173 @@ +// Copyright 2018 CoreOS, Inc. +// +// 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 v2_0 + +import ( + "fmt" + + v1 "github.com/coreos/ignition/config/v1/types" + "github.com/coreos/ignition/config/v2_0/types" + "github.com/vincent-petithory/dataurl" +) + +func TranslateFromV1(old v1.Config) types.Config { + config := types.Config{ + Ignition: types.Ignition{ + Version: types.IgnitionVersion(types.MaxVersion), + }, + } + + for _, oldDisk := range old.Storage.Disks { + disk := types.Disk{ + Device: types.Path(oldDisk.Device), + WipeTable: oldDisk.WipeTable, + } + + for _, oldPartition := range oldDisk.Partitions { + disk.Partitions = append(disk.Partitions, types.Partition{ + Label: types.PartitionLabel(oldPartition.Label), + Number: oldPartition.Number, + Size: types.PartitionDimension(oldPartition.Size), + Start: types.PartitionDimension(oldPartition.Start), + TypeGUID: types.PartitionTypeGUID(oldPartition.TypeGUID), + }) + } + + config.Storage.Disks = append(config.Storage.Disks, disk) + } + + for _, oldArray := range old.Storage.Arrays { + array := types.Raid{ + Name: oldArray.Name, + Level: oldArray.Level, + Spares: oldArray.Spares, + } + + for _, oldDevice := range oldArray.Devices { + array.Devices = append(array.Devices, types.Path(oldDevice)) + } + + config.Storage.Arrays = append(config.Storage.Arrays, array) + } + + for i, oldFilesystem := range old.Storage.Filesystems { + filesystem := types.Filesystem{ + Name: fmt.Sprintf("_translate-filesystem-%d", i), + Mount: &types.FilesystemMount{ + Device: types.Path(oldFilesystem.Device), + Format: types.FilesystemFormat(oldFilesystem.Format), + }, + } + + if oldFilesystem.Create != nil { + filesystem.Mount.Create = &types.FilesystemCreate{ + Force: oldFilesystem.Create.Force, + Options: types.MkfsOptions(oldFilesystem.Create.Options), + } + } + + config.Storage.Filesystems = append(config.Storage.Filesystems, filesystem) + + for _, oldFile := range oldFilesystem.Files { + file := types.File{ + Filesystem: filesystem.Name, + Path: types.Path(oldFile.Path), + User: types.FileUser{Id: oldFile.Uid}, + Group: types.FileGroup{Id: oldFile.Gid}, + Mode: types.FileMode(oldFile.Mode), + Contents: types.FileContents{ + Source: types.Url{ + Scheme: "data", + Opaque: "," + dataurl.EscapeString(oldFile.Contents), + }, + }, + } + + config.Storage.Files = append(config.Storage.Files, file) + } + } + + for _, oldUnit := range old.Systemd.Units { + unit := types.SystemdUnit{ + Name: types.SystemdUnitName(oldUnit.Name), + Enable: oldUnit.Enable, + Mask: oldUnit.Mask, + Contents: oldUnit.Contents, + } + + for _, oldDropIn := range oldUnit.DropIns { + unit.DropIns = append(unit.DropIns, types.SystemdUnitDropIn{ + Name: types.SystemdUnitDropInName(oldDropIn.Name), + Contents: oldDropIn.Contents, + }) + } + + config.Systemd.Units = append(config.Systemd.Units, unit) + } + + for _, oldUnit := range old.Networkd.Units { + config.Networkd.Units = append(config.Networkd.Units, types.NetworkdUnit{ + Name: types.NetworkdUnitName(oldUnit.Name), + Contents: oldUnit.Contents, + }) + } + + for _, oldUser := range old.Passwd.Users { + user := types.User{ + Name: oldUser.Name, + PasswordHash: oldUser.PasswordHash, + SSHAuthorizedKeys: oldUser.SSHAuthorizedKeys, + } + + if oldUser.Create != nil { + var uid *uint + if oldUser.Create.Uid != nil { + tmp := uint(*oldUser.Create.Uid) + uid = &tmp + } + + user.Create = &types.UserCreate{ + Uid: uid, + GECOS: oldUser.Create.GECOS, + Homedir: oldUser.Create.Homedir, + NoCreateHome: oldUser.Create.NoCreateHome, + PrimaryGroup: oldUser.Create.PrimaryGroup, + Groups: oldUser.Create.Groups, + NoUserGroup: oldUser.Create.NoUserGroup, + System: oldUser.Create.System, + NoLogInit: oldUser.Create.NoLogInit, + Shell: oldUser.Create.Shell, + } + } + + config.Passwd.Users = append(config.Passwd.Users, user) + } + + for _, oldGroup := range old.Passwd.Groups { + var gid *uint + if oldGroup.Gid != nil { + tmp := uint(*oldGroup.Gid) + gid = &tmp + } + config.Passwd.Groups = append(config.Passwd.Groups, types.Group{ + Name: oldGroup.Name, + Gid: gid, + PasswordHash: oldGroup.PasswordHash, + System: oldGroup.System, + }) + } + + return config +} diff --git a/vendor/github.com/coreos/ignition/config/v2_0/types/compression.go b/vendor/github.com/coreos/ignition/config/v2_0/types/compression.go new file mode 100644 index 00000000000..f56e5b9c80b --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v2_0/types/compression.go @@ -0,0 +1,31 @@ +// Copyright 2016 CoreOS, Inc. +// +// 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 types + +import ( + "github.com/coreos/ignition/config/shared/errors" + "github.com/coreos/ignition/config/validate/report" +) + +type Compression string + +func (c Compression) Validate() report.Report { + switch c { + case "", "gzip": + default: + return report.ReportFromError(errors.ErrCompressionInvalid, report.EntryError) + } + return report.Report{} +} diff --git a/vendor/github.com/coreos/ignition/config/v2_0/types/config.go b/vendor/github.com/coreos/ignition/config/v2_0/types/config.go new file mode 100644 index 00000000000..38551589175 --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v2_0/types/config.go @@ -0,0 +1,87 @@ +// Copyright 2015 CoreOS, Inc. +// +// 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 types + +import ( + "fmt" + + "github.com/coreos/go-semver/semver" + + "github.com/coreos/ignition/config/validate/report" +) + +var ( + MaxVersion = semver.Version{ + Major: 2, + Minor: 0, + } +) + +type Config struct { + Ignition Ignition `json:"ignition"` + Storage Storage `json:"storage,omitempty"` + Systemd Systemd `json:"systemd,omitempty"` + Networkd Networkd `json:"networkd,omitempty"` + Passwd Passwd `json:"passwd,omitempty"` +} + +func (c Config) Validate() report.Report { + r := report.Report{} + rules := []rule{ + checkFilesFilesystems, + checkDuplicateFilesystems, + } + + for _, rule := range rules { + rule(c, &r) + } + return r +} + +type rule func(cfg Config, report *report.Report) + +func checkFilesFilesystems(cfg Config, r *report.Report) { + filesystems := map[string]struct{}{"root": {}} + for _, filesystem := range cfg.Storage.Filesystems { + filesystems[filesystem.Name] = struct{}{} + } + for _, file := range cfg.Storage.Files { + if file.Filesystem == "" { + // Filesystem was not specified. This is an error, but its handled in types.File's Validate, not here + continue + } + _, ok := filesystems[file.Filesystem] + if !ok { + r.Add(report.Entry{ + Kind: report.EntryWarning, + Message: fmt.Sprintf("File %q references nonexistent filesystem %q. (This is ok if it is defined in a referenced config)", + file.Path, file.Filesystem), + }) + } + } +} + +func checkDuplicateFilesystems(cfg Config, r *report.Report) { + filesystems := map[string]struct{}{"root": {}} + for _, filesystem := range cfg.Storage.Filesystems { + if _, ok := filesystems[filesystem.Name]; ok { + r.Add(report.Entry{ + Kind: report.EntryWarning, + Message: fmt.Sprintf("Filesystem %q shadows exising filesystem definition", filesystem.Name), + }) + } + filesystems[filesystem.Name] = struct{}{} + } +} diff --git a/vendor/github.com/coreos/ignition/config/v2_0/types/disk.go b/vendor/github.com/coreos/ignition/config/v2_0/types/disk.go new file mode 100644 index 00000000000..b68c5c930ed --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v2_0/types/disk.go @@ -0,0 +1,126 @@ +// Copyright 2016 CoreOS, Inc. +// +// 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 types + +import ( + "github.com/coreos/ignition/config/shared/errors" + "github.com/coreos/ignition/config/validate/report" +) + +type Disk struct { + Device Path `json:"device,omitempty"` + WipeTable bool `json:"wipeTable,omitempty"` + Partitions []Partition `json:"partitions,omitempty"` +} + +func (n Disk) Validate() report.Report { + r := report.Report{} + if len(n.Device) == 0 { + r.Add(report.Entry{ + Message: errors.ErrDiskDeviceRequired.Error(), + Kind: report.EntryError, + }) + } + if n.partitionNumbersCollide() { + r.Add(report.Entry{ + Message: errors.ErrPartitionNumbersCollide.Error(), + Kind: report.EntryError, + }) + } + if n.partitionsOverlap() { + r.Add(report.Entry{ + Message: errors.ErrPartitionsOverlap.Error(), + Kind: report.EntryError, + }) + } + if n.partitionsMisaligned() { + r.Add(report.Entry{ + Message: errors.ErrPartitionsMisaligned.Error(), + Kind: report.EntryError, + }) + } + // Disks which have no errors at this point will likely succeed in sgdisk + return r +} + +// partitionNumbersCollide returns true if partition numbers in n.Partitions are not unique. +func (n Disk) partitionNumbersCollide() bool { + m := map[int][]Partition{} + for _, p := range n.Partitions { + if p.Number != 0 { + // a number of 0 means next available number, multiple devices can specify this + m[p.Number] = append(m[p.Number], p) + } + } + for _, n := range m { + if len(n) > 1 { + // TODO(vc): return information describing the collision for logging + return true + } + } + return false +} + +// end returns the last sector of a partition. +func (p Partition) end() PartitionDimension { + if p.Size == 0 { + // a size of 0 means "fill available", just return the start as the end for those. + return p.Start + } + return p.Start + p.Size - 1 +} + +// partitionsOverlap returns true if any explicitly dimensioned partitions overlap +func (n Disk) partitionsOverlap() bool { + for _, p := range n.Partitions { + // Starts of 0 are placed by sgdisk into the "largest available block" at that time. + // We aren't going to check those for overlap since we don't have the disk geometry. + if p.Start == 0 { + continue + } + + for _, o := range n.Partitions { + if p == o || o.Start == 0 { + continue + } + + // is p.Start within o? + if p.Start >= o.Start && p.Start <= o.end() { + return true + } + + // is p.end() within o? + if p.end() >= o.Start && p.end() <= o.end() { + return true + } + + // do p.Start and p.end() straddle o? + if p.Start < o.Start && p.end() > o.end() { + return true + } + } + } + return false +} + +// partitionsMisaligned returns true if any of the partitions don't start on a 2048-sector (1MiB) boundary. +func (n Disk) partitionsMisaligned() bool { + for _, p := range n.Partitions { + if (p.Start & (2048 - 1)) != 0 { + return true + } + } + return false +} diff --git a/vendor/github.com/coreos/ignition/config/v2_0/types/file.go b/vendor/github.com/coreos/ignition/config/v2_0/types/file.go new file mode 100644 index 00000000000..8d3e79054de --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v2_0/types/file.go @@ -0,0 +1,61 @@ +// Copyright 2016 CoreOS, Inc. +// +// 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 types + +import ( + "os" + + "github.com/coreos/ignition/config/shared/errors" + "github.com/coreos/ignition/config/validate/report" +) + +type File struct { + Filesystem string `json:"filesystem,omitempty"` + Path Path `json:"path,omitempty"` + Contents FileContents `json:"contents,omitempty"` + Mode FileMode `json:"mode,omitempty"` + User FileUser `json:"user,omitempty"` + Group FileGroup `json:"group,omitempty"` +} + +func (f File) Validate() report.Report { + if f.Filesystem == "" { + return report.ReportFromError(errors.ErrNoFilesystem, report.EntryError) + } + return report.Report{} +} + +type FileUser struct { + Id int `json:"id,omitempty"` +} + +type FileGroup struct { + Id int `json:"id,omitempty"` +} + +type FileContents struct { + Compression Compression `json:"compression,omitempty"` + Source Url `json:"source,omitempty"` + Verification Verification `json:"verification,omitempty"` +} + +type FileMode os.FileMode + +func (m FileMode) Validate() report.Report { + if (m &^ 07777) != 0 { + return report.ReportFromError(errors.ErrFileIllegalMode, report.EntryError) + } + return report.Report{} +} diff --git a/vendor/github.com/coreos/ignition/config/v2_0/types/filesystem.go b/vendor/github.com/coreos/ignition/config/v2_0/types/filesystem.go new file mode 100644 index 00000000000..e3572711da8 --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v2_0/types/filesystem.go @@ -0,0 +1,60 @@ +// Copyright 2016 CoreOS, Inc. +// +// 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 types + +import ( + "github.com/coreos/ignition/config/shared/errors" + "github.com/coreos/ignition/config/validate/report" +) + +type Filesystem struct { + Name string `json:"name,omitempty"` + Mount *FilesystemMount `json:"mount,omitempty"` + Path *Path `json:"path,omitempty"` +} + +type FilesystemMount struct { + Device Path `json:"device,omitempty"` + Format FilesystemFormat `json:"format,omitempty"` + Create *FilesystemCreate `json:"create,omitempty"` +} + +type FilesystemCreate struct { + Force bool `json:"force,omitempty"` + Options MkfsOptions `json:"options,omitempty"` +} + +func (f Filesystem) Validate() report.Report { + if f.Mount == nil && f.Path == nil { + return report.ReportFromError(errors.ErrFilesystemNoMountPath, report.EntryError) + } + if f.Mount != nil && f.Path != nil { + return report.ReportFromError(errors.ErrFilesystemMountAndPath, report.EntryError) + } + return report.Report{} +} + +type FilesystemFormat string + +func (f FilesystemFormat) Validate() report.Report { + switch f { + case "ext4", "btrfs", "xfs": + return report.Report{} + default: + return report.ReportFromError(errors.ErrFilesystemInvalidFormat, report.EntryError) + } +} + +type MkfsOptions []string diff --git a/vendor/github.com/coreos/ignition/config/v2_0/types/group.go b/vendor/github.com/coreos/ignition/config/v2_0/types/group.go new file mode 100644 index 00000000000..27e51048870 --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v2_0/types/group.go @@ -0,0 +1,22 @@ +// Copyright 2016 CoreOS, Inc. +// +// 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 types + +type Group struct { + Name string `json:"name,omitempty"` + Gid *uint `json:"gid,omitempty"` + PasswordHash string `json:"passwordHash,omitempty"` + System bool `json:"system,omitempty"` +} diff --git a/vendor/github.com/coreos/ignition/config/v2_0/types/hash.go b/vendor/github.com/coreos/ignition/config/v2_0/types/hash.go new file mode 100644 index 00000000000..628524dc6d1 --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v2_0/types/hash.go @@ -0,0 +1,72 @@ +// Copyright 2016 CoreOS, Inc. +// +// 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 types + +import ( + "crypto" + "encoding/hex" + "encoding/json" + "strings" + + "github.com/coreos/ignition/config/shared/errors" + "github.com/coreos/ignition/config/validate/report" +) + +type Hash struct { + Function string + Sum string +} + +func (h *Hash) UnmarshalJSON(data []byte) error { + var th string + if err := json.Unmarshal(data, &th); err != nil { + return err + } + + parts := strings.SplitN(th, "-", 2) + if len(parts) != 2 { + return errors.ErrHashMalformed + } + + h.Function = parts[0] + h.Sum = parts[1] + + return nil +} + +func (h Hash) MarshalJSON() ([]byte, error) { + return []byte(`"` + h.Function + "-" + h.Sum + `"`), nil +} + +func (h Hash) String() string { + bytes, _ := h.MarshalJSON() + return string(bytes) +} + +func (h Hash) Validate() report.Report { + var hash crypto.Hash + switch h.Function { + case "sha512": + hash = crypto.SHA512 + default: + return report.ReportFromError(errors.ErrHashUnrecognized, report.EntryError) + } + + if len(h.Sum) != hex.EncodedLen(hash.Size()) { + return report.ReportFromError(errors.ErrHashWrongSize, report.EntryError) + } + + return report.Report{} +} diff --git a/vendor/github.com/coreos/ignition/config/v2_0/types/ignition.go b/vendor/github.com/coreos/ignition/config/v2_0/types/ignition.go new file mode 100644 index 00000000000..deeb822d09e --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v2_0/types/ignition.go @@ -0,0 +1,64 @@ +// Copyright 2015 CoreOS, Inc. +// +// 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 types + +import ( + "encoding/json" + + "github.com/coreos/go-semver/semver" + + "github.com/coreos/ignition/config/shared/errors" + "github.com/coreos/ignition/config/validate/report" +) + +type Ignition struct { + Version IgnitionVersion `json:"version,omitempty" merge:"old"` + Config IgnitionConfig `json:"config,omitempty" merge:"new"` +} + +type IgnitionConfig struct { + Append []ConfigReference `json:"append,omitempty"` + Replace *ConfigReference `json:"replace,omitempty"` +} + +type ConfigReference struct { + Source Url `json:"source,omitempty"` + Verification Verification `json:"verification,omitempty"` +} + +type IgnitionVersion semver.Version + +func (v *IgnitionVersion) UnmarshalJSON(data []byte) error { + tv := semver.Version(*v) + if err := json.Unmarshal(data, &tv); err != nil { + return err + } + *v = IgnitionVersion(tv) + return nil +} + +func (v IgnitionVersion) MarshalJSON() ([]byte, error) { + return semver.Version(v).MarshalJSON() +} + +func (v IgnitionVersion) Validate() report.Report { + if MaxVersion.Major > v.Major { + return report.ReportFromError(errors.ErrOldVersion, report.EntryError) + } + if MaxVersion.LessThan(semver.Version(v)) { + return report.ReportFromError(errors.ErrNewVersion, report.EntryError) + } + return report.Report{} +} diff --git a/vendor/github.com/coreos/ignition/config/v2_0/types/networkd.go b/vendor/github.com/coreos/ignition/config/v2_0/types/networkd.go new file mode 100644 index 00000000000..470c721106a --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v2_0/types/networkd.go @@ -0,0 +1,19 @@ +// Copyright 2016 CoreOS, Inc. +// +// 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 types + +type Networkd struct { + Units []NetworkdUnit `json:"units,omitempty"` +} diff --git a/vendor/github.com/coreos/ignition/config/v2_0/types/partition.go b/vendor/github.com/coreos/ignition/config/v2_0/types/partition.go new file mode 100644 index 00000000000..c36545d4a63 --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v2_0/types/partition.go @@ -0,0 +1,64 @@ +// Copyright 2016 CoreOS, Inc. +// +// 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 types + +import ( + "fmt" + "regexp" + "strings" + + "github.com/coreos/ignition/config/shared/errors" + "github.com/coreos/ignition/config/validate/report" +) + +type Partition struct { + Label PartitionLabel `json:"label,omitempty"` + Number int `json:"number"` + Size PartitionDimension `json:"size"` + Start PartitionDimension `json:"start"` + TypeGUID PartitionTypeGUID `json:"typeGuid,omitempty"` +} + +type PartitionLabel string + +func (n PartitionLabel) Validate() report.Report { + // http://en.wikipedia.org/wiki/GUID_Partition_Table#Partition_entries: + // 56 (0x38) 72 bytes Partition name (36 UTF-16LE code units) + + // XXX(vc): note GPT calls it a name, we're using label for consistency + // with udev naming /dev/disk/by-partlabel/*. + if len(string(n)) > 36 { + return report.ReportFromError(errors.ErrLabelTooLong, report.EntryError) + } + if strings.Contains(string(n), ":") { + return report.ReportFromError(errors.ErrLabelContainsColon, report.EntryWarning) + } + return report.Report{} +} + +type PartitionDimension uint64 + +type PartitionTypeGUID string + +func (d PartitionTypeGUID) Validate() report.Report { + ok, err := regexp.MatchString("^(|[[:xdigit:]]{8}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{12})$", string(d)) + if err != nil { + return report.ReportFromError(fmt.Errorf("error matching type-guid regexp: %v", err), report.EntryError) + } + if !ok { + return report.ReportFromError(errors.ErrDoesntMatchGUIDRegex, report.EntryError) + } + return report.Report{} +} diff --git a/vendor/github.com/coreos/ignition/config/v2_0/types/passwd.go b/vendor/github.com/coreos/ignition/config/v2_0/types/passwd.go new file mode 100644 index 00000000000..0ffff43bb84 --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v2_0/types/passwd.go @@ -0,0 +1,20 @@ +// Copyright 2016 CoreOS, Inc. +// +// 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 types + +type Passwd struct { + Users []User `json:"users,omitempty"` + Groups []Group `json:"groups,omitempty"` +} diff --git a/vendor/github.com/coreos/ignition/config/v2_0/types/path.go b/vendor/github.com/coreos/ignition/config/v2_0/types/path.go new file mode 100644 index 00000000000..dcf35f80756 --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v2_0/types/path.go @@ -0,0 +1,35 @@ +// Copyright 2016 CoreOS, Inc. +// +// 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 types + +import ( + "path" + + "github.com/coreos/ignition/config/shared/errors" + "github.com/coreos/ignition/config/validate/report" +) + +type Path string + +func (p Path) MarshalJSON() ([]byte, error) { + return []byte(`"` + string(p) + `"`), nil +} + +func (p Path) Validate() report.Report { + if !path.IsAbs(string(p)) { + return report.ReportFromError(errors.ErrPathRelative, report.EntryError) + } + return report.Report{} +} diff --git a/vendor/github.com/coreos/ignition/config/v2_0/types/raid.go b/vendor/github.com/coreos/ignition/config/v2_0/types/raid.go new file mode 100644 index 00000000000..329b123e6d0 --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v2_0/types/raid.go @@ -0,0 +1,44 @@ +// Copyright 2016 CoreOS, Inc. +// +// 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 types + +import ( + "github.com/coreos/ignition/config/shared/errors" + "github.com/coreos/ignition/config/validate/report" +) + +type Raid struct { + Name string `json:"name"` + Level string `json:"level"` + Devices []Path `json:"devices,omitempty"` + Spares int `json:"spares,omitempty"` +} + +func (n Raid) Validate() report.Report { + switch n.Level { + case "linear", "raid0", "0", "stripe": + if n.Spares != 0 { + return report.ReportFromError(errors.ErrSparesUnsupportedForLevel, report.EntryError) + } + case "raid1", "1", "mirror": + case "raid4", "4": + case "raid5", "5": + case "raid6", "6": + case "raid10", "10": + default: + return report.ReportFromError(errors.ErrUnrecognizedRaidLevel, report.EntryError) + } + return report.Report{} +} diff --git a/vendor/github.com/coreos/ignition/config/v2_0/types/storage.go b/vendor/github.com/coreos/ignition/config/v2_0/types/storage.go new file mode 100644 index 00000000000..bd7343778a9 --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v2_0/types/storage.go @@ -0,0 +1,22 @@ +// Copyright 2016 CoreOS, Inc. +// +// 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 types + +type Storage struct { + Disks []Disk `json:"disks,omitempty"` + Arrays []Raid `json:"raid,omitempty"` + Filesystems []Filesystem `json:"filesystems,omitempty"` + Files []File `json:"files,omitempty"` +} diff --git a/vendor/github.com/coreos/ignition/config/v2_0/types/systemd.go b/vendor/github.com/coreos/ignition/config/v2_0/types/systemd.go new file mode 100644 index 00000000000..97194b91150 --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v2_0/types/systemd.go @@ -0,0 +1,19 @@ +// Copyright 2016 CoreOS, Inc. +// +// 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 types + +type Systemd struct { + Units []SystemdUnit `json:"units,omitempty"` +} diff --git a/vendor/github.com/coreos/ignition/config/v2_0/types/unit.go b/vendor/github.com/coreos/ignition/config/v2_0/types/unit.go new file mode 100644 index 00000000000..06d99f26616 --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v2_0/types/unit.go @@ -0,0 +1,115 @@ +// Copyright 2016 CoreOS, Inc. +// +// 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 types + +import ( + "fmt" + "path" + "strings" + + "github.com/coreos/go-systemd/unit" + + "github.com/coreos/ignition/config/shared/errors" + "github.com/coreos/ignition/config/shared/validations" + "github.com/coreos/ignition/config/validate/report" +) + +type SystemdUnit struct { + Name SystemdUnitName `json:"name,omitempty"` + Enable bool `json:"enable,omitempty"` + Mask bool `json:"mask,omitempty"` + Contents string `json:"contents,omitempty"` + DropIns []SystemdUnitDropIn `json:"dropins,omitempty"` +} + +func (u SystemdUnit) Validate() report.Report { + r := report.Report{} + opts, err := validateUnitContent(u.Contents) + if err != nil { + return report.ReportFromError(err, report.EntryError) + } + + r.Merge(validations.ValidateInstallSection(string(u.Name), u.Enable, u.Contents == "", opts)) + + return r +} + +type SystemdUnitDropIn struct { + Name SystemdUnitDropInName `json:"name,omitempty"` + Contents string `json:"contents,omitempty"` +} + +func (u SystemdUnitDropIn) Validate() report.Report { + if _, err := validateUnitContent(u.Contents); err != nil { + return report.ReportFromError(err, report.EntryError) + } + + return report.Report{} +} + +type SystemdUnitName string + +func (n SystemdUnitName) Validate() report.Report { + switch path.Ext(string(n)) { + case ".service", ".socket", ".device", ".mount", ".automount", ".swap", ".target", ".path", ".timer", ".snapshot", ".slice", ".scope": + return report.Report{} + default: + return report.ReportFromError(errors.ErrInvalidSystemdExt, report.EntryError) + } +} + +type SystemdUnitDropInName string + +func (n SystemdUnitDropInName) Validate() report.Report { + switch path.Ext(string(n)) { + case ".conf": + return report.Report{} + default: + return report.ReportFromError(errors.ErrInvalidSystemdDropinExt, report.EntryError) + } +} + +type NetworkdUnit struct { + Name NetworkdUnitName `json:"name,omitempty"` + Contents string `json:"contents,omitempty"` +} + +func (u NetworkdUnit) Validate() report.Report { + if _, err := validateUnitContent(u.Contents); err != nil { + return report.ReportFromError(err, report.EntryError) + } + + return report.Report{} +} + +type NetworkdUnitName string + +func (n NetworkdUnitName) Validate() report.Report { + switch path.Ext(string(n)) { + case ".link", ".netdev", ".network": + return report.Report{} + default: + return report.ReportFromError(errors.ErrInvalidNetworkdExt, report.EntryError) + } +} + +func validateUnitContent(content string) ([]*unit.UnitOption, error) { + c := strings.NewReader(content) + opts, err := unit.Deserialize(c) + if err != nil { + return nil, fmt.Errorf("invalid unit content: %s", err) + } + return opts, nil +} diff --git a/vendor/github.com/coreos/ignition/config/v2_0/types/url.go b/vendor/github.com/coreos/ignition/config/v2_0/types/url.go new file mode 100644 index 00000000000..b8ed96118be --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v2_0/types/url.go @@ -0,0 +1,69 @@ +// Copyright 2016 CoreOS, Inc. +// +// 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 types + +import ( + "encoding/json" + "net/url" + + "github.com/vincent-petithory/dataurl" + + "github.com/coreos/ignition/config/shared/errors" + "github.com/coreos/ignition/config/validate/report" +) + +type Url url.URL + +func (u *Url) UnmarshalJSON(data []byte) error { + var tu string + if err := json.Unmarshal(data, &tu); err != nil { + return err + } + + pu, err := url.Parse(tu) + if err != nil { + return errors.ErrInvalidUrl + } + + *u = Url(*pu) + return nil +} + +func (u Url) MarshalJSON() ([]byte, error) { + return []byte(`"` + u.String() + `"`), nil +} + +func (u Url) String() string { + tu := url.URL(u) + return (&tu).String() +} + +func (u Url) Validate() report.Report { + // Empty url is valid, indicates an empty file + if u.String() == "" { + return report.Report{} + } + switch url.URL(u).Scheme { + case "http", "https", "oem": + return report.Report{} + case "data": + if _, err := dataurl.DecodeString(u.String()); err != nil { + return report.ReportFromError(err, report.EntryError) + } + return report.Report{} + default: + return report.ReportFromError(errors.ErrInvalidScheme, report.EntryError) + } +} diff --git a/vendor/github.com/coreos/ignition/config/v2_0/types/user.go b/vendor/github.com/coreos/ignition/config/v2_0/types/user.go new file mode 100644 index 00000000000..f6653e27494 --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v2_0/types/user.go @@ -0,0 +1,35 @@ +// Copyright 2016 CoreOS, Inc. +// +// 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 types + +type User struct { + Name string `json:"name,omitempty"` + PasswordHash string `json:"passwordHash,omitempty"` + SSHAuthorizedKeys []string `json:"sshAuthorizedKeys,omitempty"` + Create *UserCreate `json:"create,omitempty"` +} + +type UserCreate struct { + Uid *uint `json:"uid,omitempty"` + GECOS string `json:"gecos,omitempty"` + Homedir string `json:"homeDir,omitempty"` + NoCreateHome bool `json:"noCreateHome,omitempty"` + PrimaryGroup string `json:"primaryGroup,omitempty"` + Groups []string `json:"groups,omitempty"` + NoUserGroup bool `json:"noUserGroup,omitempty"` + System bool `json:"system,omitempty"` + NoLogInit bool `json:"noLogInit,omitempty"` + Shell string `json:"shell,omitempty"` +} diff --git a/vendor/github.com/coreos/ignition/config/v2_0/types/verification.go b/vendor/github.com/coreos/ignition/config/v2_0/types/verification.go new file mode 100644 index 00000000000..b7cef403e87 --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v2_0/types/verification.go @@ -0,0 +1,19 @@ +// Copyright 2016 CoreOS, Inc. +// +// 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 types + +type Verification struct { + Hash *Hash `json:"hash,omitempty"` +} diff --git a/vendor/github.com/coreos/ignition/config/v2_1/append.go b/vendor/github.com/coreos/ignition/config/v2_1/append.go new file mode 100644 index 00000000000..b1517b73106 --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v2_1/append.go @@ -0,0 +1,72 @@ +// Copyright 2016 CoreOS, Inc. +// +// 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 v2_1 + +import ( + "reflect" + + "github.com/coreos/ignition/config/v2_1/types" +) + +// Append appends newConfig to oldConfig and returns the result. Appending one +// config to another is accomplished by iterating over every field in the +// config structure, appending slices, recursively appending structs, and +// overwriting old values with new values for all other types. +func Append(oldConfig, newConfig types.Config) types.Config { + vOld := reflect.ValueOf(oldConfig) + vNew := reflect.ValueOf(newConfig) + + vResult := appendStruct(vOld, vNew) + + return vResult.Interface().(types.Config) +} + +// appendStruct is an internal helper function to AppendConfig. Given two values +// of structures (assumed to be the same type), recursively iterate over every +// field in the struct, appending slices, recursively appending structs, and +// overwriting old values with the new for all other types. Some individual +// struct fields have alternate merge strategies, determined by the field name. +// Currently these fields are "ignition.version", which uses the old value, and +// "ignition.config" which uses the new value. +func appendStruct(vOld, vNew reflect.Value) reflect.Value { + tOld := vOld.Type() + vRes := reflect.New(tOld) + + for i := 0; i < tOld.NumField(); i++ { + vfOld := vOld.Field(i) + vfNew := vNew.Field(i) + vfRes := vRes.Elem().Field(i) + + switch tOld.Field(i).Name { + case "Version": + vfRes.Set(vfOld) + continue + case "Config": + vfRes.Set(vfNew) + continue + } + + switch vfOld.Type().Kind() { + case reflect.Struct: + vfRes.Set(appendStruct(vfOld, vfNew)) + case reflect.Slice: + vfRes.Set(reflect.AppendSlice(vfOld, vfNew)) + default: + vfRes.Set(vfNew) + } + } + + return vRes.Elem() +} diff --git a/vendor/github.com/coreos/ignition/config/v2_1/cloudinit.go b/vendor/github.com/coreos/ignition/config/v2_1/cloudinit.go new file mode 100644 index 00000000000..a019320f41d --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v2_1/cloudinit.go @@ -0,0 +1,53 @@ +// Copyright 2015 CoreOS, Inc. +// +// 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. + +// These functions are copied from github.com/coreos/coreos-cloudinit/config. + +package v2_1 + +import ( + "bytes" + "compress/gzip" + "io/ioutil" + "strings" + "unicode" +) + +func isCloudConfig(userdata []byte) bool { + header := strings.SplitN(string(decompressIfGzipped(userdata)), "\n", 2)[0] + + // Trim trailing whitespaces + header = strings.TrimRightFunc(header, unicode.IsSpace) + + return (header == "#cloud-config") +} + +func isScript(userdata []byte) bool { + header := strings.SplitN(string(decompressIfGzipped(userdata)), "\n", 2)[0] + return strings.HasPrefix(header, "#!") +} + +func decompressIfGzipped(data []byte) []byte { + if reader, err := gzip.NewReader(bytes.NewReader(data)); err == nil { + uncompressedData, err := ioutil.ReadAll(reader) + reader.Close() + if err == nil { + return uncompressedData + } else { + return data + } + } else { + return data + } +} diff --git a/vendor/github.com/coreos/ignition/config/v2_1/config.go b/vendor/github.com/coreos/ignition/config/v2_1/config.go new file mode 100644 index 00000000000..3fd271dd7e9 --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v2_1/config.go @@ -0,0 +1,68 @@ +// Copyright 2015 CoreOS, Inc. +// +// 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 v2_1 + +import ( + "github.com/coreos/go-semver/semver" + "github.com/coreos/ignition/config/shared/errors" + "github.com/coreos/ignition/config/v2_0" + "github.com/coreos/ignition/config/v2_1/types" + "github.com/coreos/ignition/config/validate" + "github.com/coreos/ignition/config/validate/report" + + json "github.com/ajeddeloh/go-json" +) + +func Parse(rawConfig []byte) (types.Config, report.Report, error) { + if isEmpty(rawConfig) { + return types.Config{}, report.Report{}, errors.ErrEmpty + } else if isCloudConfig(rawConfig) { + return types.Config{}, report.Report{}, errors.ErrCloudConfig + } else if isScript(rawConfig) { + return types.Config{}, report.Report{}, errors.ErrScript + } + + var err error + var config types.Config + + err = json.Unmarshal(rawConfig, &config) + + version, semverErr := semver.NewVersion(config.Ignition.Version) + + if err != nil || semverErr != nil || version.LessThan(types.MaxVersion) { + // We can fail unmarshaling if it's an older config. Attempt to parse + // it as such. + config, rpt, err := v2_0.Parse(rawConfig) + if err != nil { + return types.Config{}, rpt, err + } + return TranslateFromV2_0(config), rpt, err + } + + if *version != types.MaxVersion { + return types.Config{}, report.Report{}, errors.ErrUnknownVersion + } + + rpt := validate.ValidateConfig(rawConfig, config) + if rpt.IsFatal() { + return types.Config{}, rpt, errors.ErrInvalid + } + + return config, rpt, nil +} + +func isEmpty(userdata []byte) bool { + return len(userdata) == 0 +} diff --git a/vendor/github.com/coreos/ignition/config/v2_1/translate.go b/vendor/github.com/coreos/ignition/config/v2_1/translate.go new file mode 100644 index 00000000000..e6b80dd1222 --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v2_1/translate.go @@ -0,0 +1,236 @@ +// Copyright 2018 CoreOS, Inc. +// +// 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 v2_1 + +import ( + "strings" + + "github.com/coreos/ignition/config/util" + v2_0 "github.com/coreos/ignition/config/v2_0/types" + "github.com/coreos/ignition/config/v2_1/types" +) + +// golang-- +func translateV2_0MkfsOptionsTov2_1OptionSlice(opts v2_0.MkfsOptions) []types.CreateOption { + newOpts := make([]types.CreateOption, len(opts)) + for i, o := range opts { + newOpts[i] = types.CreateOption(o) + } + return newOpts +} + +// golang-- +func translateStringSliceTov2_1SSHAuthorizedKeySlice(keys []string) []types.SSHAuthorizedKey { + newKeys := make([]types.SSHAuthorizedKey, len(keys)) + for i, k := range keys { + newKeys[i] = types.SSHAuthorizedKey(k) + } + return newKeys +} + +// golang-- +func translateStringSliceTov2_1UsercreateGroupSlice(groups []string) []types.UsercreateGroup { + var newGroups []types.UsercreateGroup + for _, g := range groups { + newGroups = append(newGroups, types.UsercreateGroup(g)) + } + return newGroups +} + +func TranslateFromV2_0(old v2_0.Config) types.Config { + translateVerification := func(old v2_0.Verification) types.Verification { + var ver types.Verification + if old.Hash != nil { + // .String() here is a wrapper around MarshalJSON, which will put the hash in quotes + h := strings.Trim(old.Hash.String(), "\"") + ver.Hash = &h + } + return ver + } + translateConfigReference := func(old v2_0.ConfigReference) types.ConfigReference { + return types.ConfigReference{ + Source: old.Source.String(), + Verification: translateVerification(old.Verification), + } + } + + config := types.Config{ + Ignition: types.Ignition{ + Version: types.MaxVersion.String(), + }, + } + + if old.Ignition.Config.Replace != nil { + ref := translateConfigReference(*old.Ignition.Config.Replace) + config.Ignition.Config.Replace = &ref + } + + for _, oldAppend := range old.Ignition.Config.Append { + config.Ignition.Config.Append = + append(config.Ignition.Config.Append, translateConfigReference(oldAppend)) + } + + for _, oldDisk := range old.Storage.Disks { + disk := types.Disk{ + Device: string(oldDisk.Device), + WipeTable: oldDisk.WipeTable, + } + + for _, oldPartition := range oldDisk.Partitions { + disk.Partitions = append(disk.Partitions, types.Partition{ + Label: string(oldPartition.Label), + Number: oldPartition.Number, + Size: int(oldPartition.Size), + Start: int(oldPartition.Start), + TypeGUID: string(oldPartition.TypeGUID), + }) + } + + config.Storage.Disks = append(config.Storage.Disks, disk) + } + + for _, oldArray := range old.Storage.Arrays { + array := types.Raid{ + Name: oldArray.Name, + Level: oldArray.Level, + Spares: oldArray.Spares, + } + + for _, oldDevice := range oldArray.Devices { + array.Devices = append(array.Devices, types.Device(oldDevice)) + } + + config.Storage.Raid = append(config.Storage.Raid, array) + } + + for _, oldFilesystem := range old.Storage.Filesystems { + filesystem := types.Filesystem{ + Name: oldFilesystem.Name, + } + + if oldFilesystem.Mount != nil { + filesystem.Mount = &types.Mount{ + Device: string(oldFilesystem.Mount.Device), + Format: string(oldFilesystem.Mount.Format), + } + + if oldFilesystem.Mount.Create != nil { + filesystem.Mount.Create = &types.Create{ + Force: oldFilesystem.Mount.Create.Force, + Options: translateV2_0MkfsOptionsTov2_1OptionSlice(oldFilesystem.Mount.Create.Options), + } + } + } + + if oldFilesystem.Path != nil { + p := string(*oldFilesystem.Path) + filesystem.Path = &p + } + + config.Storage.Filesystems = append(config.Storage.Filesystems, filesystem) + } + + for _, oldFile := range old.Storage.Files { + file := types.File{ + Node: types.Node{ + Filesystem: oldFile.Filesystem, + Path: string(oldFile.Path), + User: types.NodeUser{ID: util.IntToPtr(oldFile.User.Id)}, + Group: types.NodeGroup{ID: util.IntToPtr(oldFile.Group.Id)}, + }, + FileEmbedded1: types.FileEmbedded1{ + Mode: int(oldFile.Mode), + Contents: types.FileContents{ + Compression: string(oldFile.Contents.Compression), + Source: oldFile.Contents.Source.String(), + Verification: translateVerification(oldFile.Contents.Verification), + }, + }, + } + + config.Storage.Files = append(config.Storage.Files, file) + } + + for _, oldUnit := range old.Systemd.Units { + unit := types.Unit{ + Name: string(oldUnit.Name), + Enable: oldUnit.Enable, + Mask: oldUnit.Mask, + Contents: oldUnit.Contents, + } + + for _, oldDropIn := range oldUnit.DropIns { + unit.Dropins = append(unit.Dropins, types.Dropin{ + Name: string(oldDropIn.Name), + Contents: oldDropIn.Contents, + }) + } + + config.Systemd.Units = append(config.Systemd.Units, unit) + } + + for _, oldUnit := range old.Networkd.Units { + config.Networkd.Units = append(config.Networkd.Units, types.Networkdunit{ + Name: string(oldUnit.Name), + Contents: oldUnit.Contents, + }) + } + + for _, oldUser := range old.Passwd.Users { + user := types.PasswdUser{ + Name: oldUser.Name, + PasswordHash: util.StrToPtr(oldUser.PasswordHash), + SSHAuthorizedKeys: translateStringSliceTov2_1SSHAuthorizedKeySlice(oldUser.SSHAuthorizedKeys), + } + + if oldUser.Create != nil { + var u *int + if oldUser.Create.Uid != nil { + tmp := int(*oldUser.Create.Uid) + u = &tmp + } + user.Create = &types.Usercreate{ + UID: u, + Gecos: oldUser.Create.GECOS, + HomeDir: oldUser.Create.Homedir, + NoCreateHome: oldUser.Create.NoCreateHome, + PrimaryGroup: oldUser.Create.PrimaryGroup, + Groups: translateStringSliceTov2_1UsercreateGroupSlice(oldUser.Create.Groups), + NoUserGroup: oldUser.Create.NoUserGroup, + System: oldUser.Create.System, + NoLogInit: oldUser.Create.NoLogInit, + Shell: oldUser.Create.Shell, + } + } + + config.Passwd.Users = append(config.Passwd.Users, user) + } + + for _, oldGroup := range old.Passwd.Groups { + var g *int + if oldGroup.Gid != nil { + tmp := int(*oldGroup.Gid) + g = &tmp + } + config.Passwd.Groups = append(config.Passwd.Groups, types.PasswdGroup{ + Name: oldGroup.Name, + Gid: g, + PasswordHash: oldGroup.PasswordHash, + System: oldGroup.System, + }) + } + + return config +} diff --git a/vendor/github.com/coreos/ignition/config/v2_1/types/config.go b/vendor/github.com/coreos/ignition/config/v2_1/types/config.go new file mode 100644 index 00000000000..0e83bc6ad10 --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v2_1/types/config.go @@ -0,0 +1,91 @@ +// Copyright 2015 CoreOS, Inc. +// +// 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 types + +import ( + "fmt" + + "github.com/coreos/go-semver/semver" + + "github.com/coreos/ignition/config/validate/report" +) + +var ( + MaxVersion = semver.Version{ + Major: 2, + Minor: 1, + } +) + +func (c Config) Validate() report.Report { + r := report.Report{} + rules := []rule{ + checkFilesFilesystems, + checkDuplicateFilesystems, + } + + for _, rule := range rules { + rule(c, &r) + } + return r +} + +type rule func(cfg Config, report *report.Report) + +func checkNodeFilesystems(node Node, filesystems map[string]struct{}, nodeType string) report.Report { + r := report.Report{} + if node.Filesystem == "" { + // Filesystem was not specified. This is an error, but its handled in types.File's Validate, not here + return r + } + _, ok := filesystems[node.Filesystem] + if !ok { + r.Add(report.Entry{ + Kind: report.EntryWarning, + Message: fmt.Sprintf("%v %q references nonexistent filesystem %q. (This is ok if it is defined in a referenced config)", + nodeType, node.Path, node.Filesystem), + }) + } + return r +} + +func checkFilesFilesystems(cfg Config, r *report.Report) { + filesystems := map[string]struct{}{"root": {}} + for _, filesystem := range cfg.Storage.Filesystems { + filesystems[filesystem.Name] = struct{}{} + } + for _, file := range cfg.Storage.Files { + r.Merge(checkNodeFilesystems(file.Node, filesystems, "File")) + } + for _, link := range cfg.Storage.Links { + r.Merge(checkNodeFilesystems(link.Node, filesystems, "Link")) + } + for _, dir := range cfg.Storage.Directories { + r.Merge(checkNodeFilesystems(dir.Node, filesystems, "Directory")) + } +} + +func checkDuplicateFilesystems(cfg Config, r *report.Report) { + filesystems := map[string]struct{}{"root": {}} + for _, filesystem := range cfg.Storage.Filesystems { + if _, ok := filesystems[filesystem.Name]; ok { + r.Add(report.Entry{ + Kind: report.EntryWarning, + Message: fmt.Sprintf("Filesystem %q shadows exising filesystem definition", filesystem.Name), + }) + } + filesystems[filesystem.Name] = struct{}{} + } +} diff --git a/vendor/github.com/coreos/ignition/config/v2_1/types/directory.go b/vendor/github.com/coreos/ignition/config/v2_1/types/directory.go new file mode 100644 index 00000000000..16adad05910 --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v2_1/types/directory.go @@ -0,0 +1,30 @@ +// Copyright 2017 CoreOS, Inc. +// +// 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 types + +import ( + "github.com/coreos/ignition/config/validate/report" +) + +func (d Directory) ValidateMode() report.Report { + r := report.Report{} + if err := validateMode(d.Mode); err != nil { + r.Add(report.Entry{ + Message: err.Error(), + Kind: report.EntryError, + }) + } + return r +} diff --git a/vendor/github.com/coreos/ignition/config/v2_1/types/disk.go b/vendor/github.com/coreos/ignition/config/v2_1/types/disk.go new file mode 100644 index 00000000000..f0af504a171 --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v2_1/types/disk.go @@ -0,0 +1,128 @@ +// Copyright 2016 CoreOS, Inc. +// +// 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 types + +import ( + "github.com/coreos/ignition/config/shared/errors" + "github.com/coreos/ignition/config/validate/report" +) + +func (n Disk) Validate() report.Report { + return report.Report{} +} + +func (n Disk) ValidateDevice() report.Report { + if len(n.Device) == 0 { + return report.ReportFromError(errors.ErrDiskDeviceRequired, report.EntryError) + } + if err := validatePath(string(n.Device)); err != nil { + return report.ReportFromError(err, report.EntryError) + } + return report.Report{} +} + +func (n Disk) ValidatePartitions() report.Report { + r := report.Report{} + if n.partitionNumbersCollide() { + r.Add(report.Entry{ + Message: errors.ErrPartitionNumbersCollide.Error(), + Kind: report.EntryError, + }) + } + if n.partitionsOverlap() { + r.Add(report.Entry{ + Message: errors.ErrPartitionsOverlap.Error(), + Kind: report.EntryError, + }) + } + if n.partitionsMisaligned() { + r.Add(report.Entry{ + Message: errors.ErrPartitionsMisaligned.Error(), + Kind: report.EntryError, + }) + } + // Disks which have no errors at this point will likely succeed in sgdisk + return r +} + +// partitionNumbersCollide returns true if partition numbers in n.Partitions are not unique. +func (n Disk) partitionNumbersCollide() bool { + m := map[int][]Partition{} + for _, p := range n.Partitions { + if p.Number != 0 { + // a number of 0 means next available number, multiple devices can specify this + m[p.Number] = append(m[p.Number], p) + } + } + for _, n := range m { + if len(n) > 1 { + // TODO(vc): return information describing the collision for logging + return true + } + } + return false +} + +// end returns the last sector of a partition. +func (p Partition) end() int { + if p.Size == 0 { + // a size of 0 means "fill available", just return the start as the end for those. + return p.Start + } + return p.Start + p.Size - 1 +} + +// partitionsOverlap returns true if any explicitly dimensioned partitions overlap +func (n Disk) partitionsOverlap() bool { + for _, p := range n.Partitions { + // Starts of 0 are placed by sgdisk into the "largest available block" at that time. + // We aren't going to check those for overlap since we don't have the disk geometry. + if p.Start == 0 { + continue + } + + for _, o := range n.Partitions { + if p == o || o.Start == 0 { + continue + } + + // is p.Start within o? + if p.Start >= o.Start && p.Start <= o.end() { + return true + } + + // is p.end() within o? + if p.end() >= o.Start && p.end() <= o.end() { + return true + } + + // do p.Start and p.end() straddle o? + if p.Start < o.Start && p.end() > o.end() { + return true + } + } + } + return false +} + +// partitionsMisaligned returns true if any of the partitions don't start on a 2048-sector (1MiB) boundary. +func (n Disk) partitionsMisaligned() bool { + for _, p := range n.Partitions { + if (p.Start & (2048 - 1)) != 0 { + return true + } + } + return false +} diff --git a/vendor/github.com/coreos/ignition/config/v2_1/types/file.go b/vendor/github.com/coreos/ignition/config/v2_1/types/file.go new file mode 100644 index 00000000000..ac79cd85854 --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v2_1/types/file.go @@ -0,0 +1,56 @@ +// Copyright 2016 CoreOS, Inc. +// +// 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 types + +import ( + "github.com/coreos/ignition/config/shared/errors" + "github.com/coreos/ignition/config/validate/report" +) + +func (f File) ValidateMode() report.Report { + r := report.Report{} + if err := validateMode(f.Mode); err != nil { + r.Add(report.Entry{ + Message: err.Error(), + Kind: report.EntryError, + }) + } + return r +} + +func (fc FileContents) ValidateCompression() report.Report { + r := report.Report{} + switch fc.Compression { + case "", "gzip": + default: + r.Add(report.Entry{ + Message: errors.ErrCompressionInvalid.Error(), + Kind: report.EntryError, + }) + } + return r +} + +func (fc FileContents) ValidateSource() report.Report { + r := report.Report{} + err := validateURL(fc.Source) + if err != nil { + r.Add(report.Entry{ + Message: err.Error(), + Kind: report.EntryError, + }) + } + return r +} diff --git a/vendor/github.com/coreos/ignition/config/v2_1/types/filesystem.go b/vendor/github.com/coreos/ignition/config/v2_1/types/filesystem.go new file mode 100644 index 00000000000..a2e43ffda1d --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v2_1/types/filesystem.go @@ -0,0 +1,144 @@ +// Copyright 2016 CoreOS, Inc. +// +// 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 types + +import ( + "github.com/coreos/ignition/config/shared/errors" + "github.com/coreos/ignition/config/validate/report" +) + +func (f Filesystem) Validate() report.Report { + r := report.Report{} + if f.Mount == nil && f.Path == nil { + r.Add(report.Entry{ + Message: errors.ErrFilesystemNoMountPath.Error(), + Kind: report.EntryError, + }) + } + if f.Mount != nil { + if f.Path != nil { + r.Add(report.Entry{ + Message: errors.ErrFilesystemMountAndPath.Error(), + Kind: report.EntryError, + }) + } + if f.Mount.Create != nil { + if f.Mount.WipeFilesystem { + r.Add(report.Entry{ + Message: errors.ErrUsedCreateAndWipeFilesystem.Error(), + Kind: report.EntryError, + }) + } + if len(f.Mount.Options) > 0 { + r.Add(report.Entry{ + Message: errors.ErrUsedCreateAndMountOpts.Error(), + Kind: report.EntryError, + }) + } + r.Add(report.Entry{ + Message: errors.ErrWarningCreateDeprecated.Error(), + Kind: report.EntryWarning, + }) + } + } + return r +} + +func (f Filesystem) ValidatePath() report.Report { + r := report.Report{} + if f.Path != nil && validatePath(*f.Path) != nil { + r.Add(report.Entry{ + Message: errors.ErrPathRelative.Error(), + Kind: report.EntryError, + }) + } + return r +} + +func (m Mount) Validate() report.Report { + r := report.Report{} + switch m.Format { + case "ext4", "btrfs", "xfs", "swap", "vfat": + default: + r.Add(report.Entry{ + Message: errors.ErrFilesystemInvalidFormat.Error(), + Kind: report.EntryError, + }) + } + return r +} + +func (m Mount) ValidateDevice() report.Report { + r := report.Report{} + if err := validatePath(m.Device); err != nil { + r.Add(report.Entry{ + Message: err.Error(), + Kind: report.EntryError, + }) + } + return r +} + +func (m Mount) ValidateLabel() report.Report { + r := report.Report{} + if m.Label == nil { + return r + } + switch m.Format { + case "ext4": + if len(*m.Label) > 16 { + // source: man mkfs.ext4 + r.Add(report.Entry{ + Message: errors.ErrExt4LabelTooLong.Error(), + Kind: report.EntryError, + }) + } + case "btrfs": + if len(*m.Label) > 256 { + // source: man mkfs.btrfs + r.Add(report.Entry{ + Message: errors.ErrBtrfsLabelTooLong.Error(), + Kind: report.EntryError, + }) + } + case "xfs": + if len(*m.Label) > 12 { + // source: man mkfs.xfs + r.Add(report.Entry{ + Message: errors.ErrXfsLabelTooLong.Error(), + Kind: report.EntryError, + }) + } + case "swap": + // mkswap's man page does not state a limit on label size, but through + // experimentation it appears that mkswap will truncate long labels to + // 15 characters, so let's enforce that. + if len(*m.Label) > 15 { + r.Add(report.Entry{ + Message: errors.ErrSwapLabelTooLong.Error(), + Kind: report.EntryError, + }) + } + case "vfat": + if len(*m.Label) > 11 { + // source: man mkfs.fat + r.Add(report.Entry{ + Message: errors.ErrVfatLabelTooLong.Error(), + Kind: report.EntryError, + }) + } + } + return r +} diff --git a/vendor/github.com/coreos/ignition/config/v2_1/types/ignition.go b/vendor/github.com/coreos/ignition/config/v2_1/types/ignition.go new file mode 100644 index 00000000000..bddf4958338 --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v2_1/types/ignition.go @@ -0,0 +1,52 @@ +// Copyright 2015 CoreOS, Inc. +// +// 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 types + +import ( + "github.com/coreos/go-semver/semver" + + "github.com/coreos/ignition/config/shared/errors" + "github.com/coreos/ignition/config/validate/report" +) + +func (c ConfigReference) ValidateSource() report.Report { + r := report.Report{} + err := validateURL(c.Source) + if err != nil { + r.Add(report.Entry{ + Message: err.Error(), + Kind: report.EntryError, + }) + } + return r +} + +func (v Ignition) Semver() (*semver.Version, error) { + return semver.NewVersion(v.Version) +} + +func (v Ignition) Validate() report.Report { + tv, err := v.Semver() + if err != nil { + return report.ReportFromError(errors.ErrInvalidVersion, report.EntryError) + } + if MaxVersion.Major > tv.Major { + return report.ReportFromError(errors.ErrOldVersion, report.EntryError) + } + if MaxVersion.LessThan(*tv) { + return report.ReportFromError(errors.ErrNewVersion, report.EntryError) + } + return report.Report{} +} diff --git a/vendor/github.com/coreos/ignition/config/v2_1/types/link.go b/vendor/github.com/coreos/ignition/config/v2_1/types/link.go new file mode 100644 index 00000000000..f0284425282 --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v2_1/types/link.go @@ -0,0 +1,33 @@ +// Copyright 2017 CoreOS, Inc. +// +// 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 types + +import ( + "github.com/coreos/ignition/config/validate/report" +) + +func (s LinkEmbedded1) ValidateTarget() report.Report { + r := report.Report{} + if !s.Hard { + err := validatePath(s.Target) + if err != nil { + r.Add(report.Entry{ + Message: err.Error(), + Kind: report.EntryError, + }) + } + } + return r +} diff --git a/vendor/github.com/coreos/ignition/config/v2_1/types/mode.go b/vendor/github.com/coreos/ignition/config/v2_1/types/mode.go new file mode 100644 index 00000000000..12d4188e95f --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v2_1/types/mode.go @@ -0,0 +1,26 @@ +// Copyright 2017 CoreOS, Inc. +// +// 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 types + +import ( + "github.com/coreos/ignition/config/shared/errors" +) + +func validateMode(m int) error { + if m < 0 || m > 07777 { + return errors.ErrFileIllegalMode + } + return nil +} diff --git a/vendor/github.com/coreos/ignition/config/v2_1/types/node.go b/vendor/github.com/coreos/ignition/config/v2_1/types/node.go new file mode 100644 index 00000000000..50badfdfb92 --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v2_1/types/node.go @@ -0,0 +1,73 @@ +// Copyright 2016 CoreOS, Inc. +// +// 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 types + +import ( + "path/filepath" + + "github.com/coreos/ignition/config/shared/errors" + "github.com/coreos/ignition/config/validate/report" +) + +func (n Node) ValidateFilesystem() report.Report { + r := report.Report{} + if n.Filesystem == "" { + r.Add(report.Entry{ + Message: errors.ErrNoFilesystem.Error(), + Kind: report.EntryError, + }) + } + return r +} + +func (n Node) ValidatePath() report.Report { + r := report.Report{} + if err := validatePath(n.Path); err != nil { + r.Add(report.Entry{ + Message: err.Error(), + Kind: report.EntryError, + }) + } + return r +} + +func (n Node) Depth() int { + count := 0 + for p := filepath.Clean(string(n.Path)); p != "/"; count++ { + p = filepath.Dir(p) + } + return count +} + +func (nu NodeUser) Validate() report.Report { + r := report.Report{} + if nu.ID != nil && nu.Name != "" { + r.Add(report.Entry{ + Message: errors.ErrBothIDAndNameSet.Error(), + Kind: report.EntryError, + }) + } + return r +} +func (ng NodeGroup) Validate() report.Report { + r := report.Report{} + if ng.ID != nil && ng.Name != "" { + r.Add(report.Entry{ + Message: errors.ErrBothIDAndNameSet.Error(), + Kind: report.EntryError, + }) + } + return r +} diff --git a/vendor/github.com/coreos/ignition/config/v2_1/types/partition.go b/vendor/github.com/coreos/ignition/config/v2_1/types/partition.go new file mode 100644 index 00000000000..084dce7ce23 --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v2_1/types/partition.go @@ -0,0 +1,77 @@ +// Copyright 2016 CoreOS, Inc. +// +// 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 types + +import ( + "fmt" + "regexp" + "strings" + + "github.com/coreos/ignition/config/shared/errors" + "github.com/coreos/ignition/config/validate/report" +) + +const ( + guidRegexStr = "^(|[[:xdigit:]]{8}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{12})$" +) + +func (p Partition) ValidateLabel() report.Report { + r := report.Report{} + // http://en.wikipedia.org/wiki/GUID_Partition_Table#Partition_entries: + // 56 (0x38) 72 bytes Partition name (36 UTF-16LE code units) + + // XXX(vc): note GPT calls it a name, we're using label for consistency + // with udev naming /dev/disk/by-partlabel/*. + if len(p.Label) > 36 { + r.Add(report.Entry{ + Message: errors.ErrLabelTooLong.Error(), + Kind: report.EntryError, + }) + } + + // sgdisk uses colons for delimitting compound arguments and does not allow escaping them. + if strings.Contains(p.Label, ":") { + r.Add(report.Entry{ + Message: errors.ErrLabelContainsColon.Error(), + Kind: report.EntryWarning, + }) + } + return r +} + +func (p Partition) ValidateTypeGUID() report.Report { + return validateGUID(p.TypeGUID) +} + +func (p Partition) ValidateGUID() report.Report { + return validateGUID(p.GUID) +} + +func validateGUID(guid string) report.Report { + r := report.Report{} + ok, err := regexp.MatchString(guidRegexStr, guid) + if err != nil { + r.Add(report.Entry{ + Message: fmt.Sprintf("error matching guid regexp: %v", err), + Kind: report.EntryError, + }) + } else if !ok { + r.Add(report.Entry{ + Message: errors.ErrDoesntMatchGUIDRegex.Error(), + Kind: report.EntryError, + }) + } + return r +} diff --git a/vendor/github.com/coreos/ignition/config/v2_1/types/passwd.go b/vendor/github.com/coreos/ignition/config/v2_1/types/passwd.go new file mode 100644 index 00000000000..10508c56c06 --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v2_1/types/passwd.go @@ -0,0 +1,67 @@ +// Copyright 2017 CoreOS, Inc. +// +// 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 types + +import ( + "github.com/coreos/ignition/config/shared/errors" + "github.com/coreos/ignition/config/validate/report" +) + +func (p PasswdUser) Validate() report.Report { + r := report.Report{} + if p.Create != nil { + r.Add(report.Entry{ + Message: errors.ErrPasswdCreateDeprecated.Error(), + Kind: report.EntryWarning, + }) + addErr := func(err error) { + r.Add(report.Entry{ + Message: err.Error(), + Kind: report.EntryError, + }) + } + if p.Gecos != "" { + addErr(errors.ErrPasswdCreateAndGecos) + } + if len(p.Groups) > 0 { + addErr(errors.ErrPasswdCreateAndGroups) + } + if p.HomeDir != "" { + addErr(errors.ErrPasswdCreateAndHomeDir) + } + if p.NoCreateHome { + addErr(errors.ErrPasswdCreateAndNoCreateHome) + } + if p.NoLogInit { + addErr(errors.ErrPasswdCreateAndNoLogInit) + } + if p.NoUserGroup { + addErr(errors.ErrPasswdCreateAndNoUserGroup) + } + if p.PrimaryGroup != "" { + addErr(errors.ErrPasswdCreateAndPrimaryGroup) + } + if p.Shell != "" { + addErr(errors.ErrPasswdCreateAndShell) + } + if p.System { + addErr(errors.ErrPasswdCreateAndSystem) + } + if p.UID != nil { + addErr(errors.ErrPasswdCreateAndUID) + } + } + return r +} diff --git a/vendor/github.com/coreos/ignition/config/v2_1/types/path.go b/vendor/github.com/coreos/ignition/config/v2_1/types/path.go new file mode 100644 index 00000000000..780607c31ab --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v2_1/types/path.go @@ -0,0 +1,28 @@ +// Copyright 2016 CoreOS, Inc. +// +// 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 types + +import ( + "path" + + "github.com/coreos/ignition/config/shared/errors" +) + +func validatePath(p string) error { + if !path.IsAbs(p) { + return errors.ErrPathRelative + } + return nil +} diff --git a/vendor/github.com/coreos/ignition/config/v2_1/types/raid.go b/vendor/github.com/coreos/ignition/config/v2_1/types/raid.go new file mode 100644 index 00000000000..3aceaa9faa1 --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v2_1/types/raid.go @@ -0,0 +1,57 @@ +// Copyright 2016 CoreOS, Inc. +// +// 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 types + +import ( + "github.com/coreos/ignition/config/shared/errors" + "github.com/coreos/ignition/config/validate/report" +) + +func (n Raid) ValidateLevel() report.Report { + r := report.Report{} + switch n.Level { + case "linear", "raid0", "0", "stripe": + if n.Spares != 0 { + r.Add(report.Entry{ + Message: errors.ErrSparesUnsupportedForLevel.Error(), + Kind: report.EntryError, + }) + } + case "raid1", "1", "mirror": + case "raid4", "4": + case "raid5", "5": + case "raid6", "6": + case "raid10", "10": + default: + r.Add(report.Entry{ + Message: errors.ErrUnrecognizedRaidLevel.Error(), + Kind: report.EntryError, + }) + } + return r +} + +func (n Raid) ValidateDevices() report.Report { + r := report.Report{} + for _, d := range n.Devices { + if err := validatePath(string(d)); err != nil { + r.Add(report.Entry{ + Message: errors.ErrPathRelative.Error(), + Kind: report.EntryError, + }) + } + } + return r +} diff --git a/vendor/github.com/coreos/ignition/config/v2_1/types/schema.go b/vendor/github.com/coreos/ignition/config/v2_1/types/schema.go new file mode 100644 index 00000000000..e0caed5e6e2 --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v2_1/types/schema.go @@ -0,0 +1,221 @@ +package types + +// generated by "schematyper --package=types schema/ignition.json -o config/types/schema.go --root-type=Config" -- DO NOT EDIT + +type Config struct { + Ignition Ignition `json:"ignition"` + Networkd Networkd `json:"networkd,omitempty"` + Passwd Passwd `json:"passwd,omitempty"` + Storage Storage `json:"storage,omitempty"` + Systemd Systemd `json:"systemd,omitempty"` +} + +type ConfigReference struct { + Source string `json:"source,omitempty"` + Verification Verification `json:"verification,omitempty"` +} + +type Create struct { + Force bool `json:"force,omitempty"` + Options []CreateOption `json:"options,omitempty"` +} + +type CreateOption string + +type Device string + +type Directory struct { + Node + DirectoryEmbedded1 +} + +type DirectoryEmbedded1 struct { + Mode int `json:"mode,omitempty"` +} + +type Disk struct { + Device string `json:"device,omitempty"` + Partitions []Partition `json:"partitions,omitempty"` + WipeTable bool `json:"wipeTable,omitempty"` +} + +type Dropin struct { + Contents string `json:"contents,omitempty"` + Name string `json:"name,omitempty"` +} + +type File struct { + Node + FileEmbedded1 +} + +type FileContents struct { + Compression string `json:"compression,omitempty"` + Source string `json:"source,omitempty"` + Verification Verification `json:"verification,omitempty"` +} + +type FileEmbedded1 struct { + Contents FileContents `json:"contents,omitempty"` + Mode int `json:"mode,omitempty"` +} + +type Filesystem struct { + Mount *Mount `json:"mount,omitempty"` + Name string `json:"name,omitempty"` + Path *string `json:"path,omitempty"` +} + +type Ignition struct { + Config IgnitionConfig `json:"config,omitempty"` + Timeouts Timeouts `json:"timeouts,omitempty"` + Version string `json:"version,omitempty"` +} + +type IgnitionConfig struct { + Append []ConfigReference `json:"append,omitempty"` + Replace *ConfigReference `json:"replace,omitempty"` +} + +type Link struct { + Node + LinkEmbedded1 +} + +type LinkEmbedded1 struct { + Hard bool `json:"hard,omitempty"` + Target string `json:"target,omitempty"` +} + +type Mount struct { + Create *Create `json:"create,omitempty"` + Device string `json:"device,omitempty"` + Format string `json:"format,omitempty"` + Label *string `json:"label,omitempty"` + Options []MountOption `json:"options,omitempty"` + UUID *string `json:"uuid,omitempty"` + WipeFilesystem bool `json:"wipeFilesystem,omitempty"` +} + +type MountOption string + +type Networkd struct { + Units []Networkdunit `json:"units,omitempty"` +} + +type Networkdunit struct { + Contents string `json:"contents,omitempty"` + Name string `json:"name,omitempty"` +} + +type Node struct { + Filesystem string `json:"filesystem,omitempty"` + Group NodeGroup `json:"group,omitempty"` + Path string `json:"path,omitempty"` + User NodeUser `json:"user,omitempty"` +} + +type NodeGroup struct { + ID *int `json:"id,omitempty"` + Name string `json:"name,omitempty"` +} + +type NodeUser struct { + ID *int `json:"id,omitempty"` + Name string `json:"name,omitempty"` +} + +type Partition struct { + GUID string `json:"guid,omitempty"` + Label string `json:"label,omitempty"` + Number int `json:"number,omitempty"` + Size int `json:"size,omitempty"` + Start int `json:"start,omitempty"` + TypeGUID string `json:"typeGuid,omitempty"` +} + +type Passwd struct { + Groups []PasswdGroup `json:"groups,omitempty"` + Users []PasswdUser `json:"users,omitempty"` +} + +type PasswdGroup struct { + Gid *int `json:"gid,omitempty"` + Name string `json:"name,omitempty"` + PasswordHash string `json:"passwordHash,omitempty"` + System bool `json:"system,omitempty"` +} + +type PasswdUser struct { + Create *Usercreate `json:"create,omitempty"` + Gecos string `json:"gecos,omitempty"` + Groups []PasswdUserGroup `json:"groups,omitempty"` + HomeDir string `json:"homeDir,omitempty"` + Name string `json:"name,omitempty"` + NoCreateHome bool `json:"noCreateHome,omitempty"` + NoLogInit bool `json:"noLogInit,omitempty"` + NoUserGroup bool `json:"noUserGroup,omitempty"` + PasswordHash *string `json:"passwordHash,omitempty"` + PrimaryGroup string `json:"primaryGroup,omitempty"` + SSHAuthorizedKeys []SSHAuthorizedKey `json:"sshAuthorizedKeys,omitempty"` + Shell string `json:"shell,omitempty"` + System bool `json:"system,omitempty"` + UID *int `json:"uid,omitempty"` +} + +type PasswdUserGroup string + +type Raid struct { + Devices []Device `json:"devices,omitempty"` + Level string `json:"level,omitempty"` + Name string `json:"name,omitempty"` + Spares int `json:"spares,omitempty"` +} + +type SSHAuthorizedKey string + +type Storage struct { + Directories []Directory `json:"directories,omitempty"` + Disks []Disk `json:"disks,omitempty"` + Files []File `json:"files,omitempty"` + Filesystems []Filesystem `json:"filesystems,omitempty"` + Links []Link `json:"links,omitempty"` + Raid []Raid `json:"raid,omitempty"` +} + +type Systemd struct { + Units []Unit `json:"units,omitempty"` +} + +type Timeouts struct { + HTTPResponseHeaders *int `json:"httpResponseHeaders,omitempty"` + HTTPTotal *int `json:"httpTotal,omitempty"` +} + +type Unit struct { + Contents string `json:"contents,omitempty"` + Dropins []Dropin `json:"dropins,omitempty"` + Enable bool `json:"enable,omitempty"` + Enabled *bool `json:"enabled,omitempty"` + Mask bool `json:"mask,omitempty"` + Name string `json:"name,omitempty"` +} + +type Usercreate struct { + Gecos string `json:"gecos,omitempty"` + Groups []UsercreateGroup `json:"groups,omitempty"` + HomeDir string `json:"homeDir,omitempty"` + NoCreateHome bool `json:"noCreateHome,omitempty"` + NoLogInit bool `json:"noLogInit,omitempty"` + NoUserGroup bool `json:"noUserGroup,omitempty"` + PrimaryGroup string `json:"primaryGroup,omitempty"` + Shell string `json:"shell,omitempty"` + System bool `json:"system,omitempty"` + UID *int `json:"uid,omitempty"` +} + +type UsercreateGroup string + +type Verification struct { + Hash *string `json:"hash,omitempty"` +} diff --git a/vendor/github.com/coreos/ignition/config/v2_1/types/unit.go b/vendor/github.com/coreos/ignition/config/v2_1/types/unit.go new file mode 100644 index 00000000000..07e6fe6f5cc --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v2_1/types/unit.go @@ -0,0 +1,109 @@ +// Copyright 2016 CoreOS, Inc. +// +// 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 types + +import ( + "fmt" + "path" + "strings" + + "github.com/coreos/go-systemd/unit" + + "github.com/coreos/ignition/config/shared/errors" + "github.com/coreos/ignition/config/shared/validations" + "github.com/coreos/ignition/config/validate/report" +) + +func (u Unit) ValidateContents() report.Report { + r := report.Report{} + opts, err := validateUnitContent(u.Contents) + if err != nil { + r.Add(report.Entry{ + Message: err.Error(), + Kind: report.EntryError, + }) + } + + isEnabled := u.Enable || (u.Enabled != nil && *u.Enabled) + r.Merge(validations.ValidateInstallSection(u.Name, isEnabled, u.Contents == "", opts)) + + return r +} + +func (u Unit) ValidateName() report.Report { + r := report.Report{} + switch path.Ext(u.Name) { + case ".service", ".socket", ".device", ".mount", ".automount", ".swap", ".target", ".path", ".timer", ".snapshot", ".slice", ".scope": + default: + r.Add(report.Entry{ + Message: errors.ErrInvalidSystemdExt.Error(), + Kind: report.EntryError, + }) + } + return r +} + +func (d Dropin) Validate() report.Report { + r := report.Report{} + + if _, err := validateUnitContent(d.Contents); err != nil { + r.Add(report.Entry{ + Message: err.Error(), + Kind: report.EntryError, + }) + } + + switch path.Ext(d.Name) { + case ".conf": + default: + r.Add(report.Entry{ + Message: errors.ErrInvalidSystemdDropinExt.Error(), + Kind: report.EntryError, + }) + } + + return r +} + +func (u Networkdunit) Validate() report.Report { + r := report.Report{} + + if _, err := validateUnitContent(u.Contents); err != nil { + r.Add(report.Entry{ + Message: err.Error(), + Kind: report.EntryError, + }) + } + + switch path.Ext(u.Name) { + case ".link", ".netdev", ".network": + default: + r.Add(report.Entry{ + Message: errors.ErrInvalidNetworkdExt.Error(), + Kind: report.EntryError, + }) + } + + return r +} + +func validateUnitContent(content string) ([]*unit.UnitOption, error) { + c := strings.NewReader(content) + opts, err := unit.Deserialize(c) + if err != nil { + return nil, fmt.Errorf("invalid unit content: %s", err) + } + return opts, nil +} diff --git a/vendor/github.com/coreos/ignition/config/v2_1/types/url.go b/vendor/github.com/coreos/ignition/config/v2_1/types/url.go new file mode 100644 index 00000000000..0fdc4a170ef --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v2_1/types/url.go @@ -0,0 +1,45 @@ +// Copyright 2016 CoreOS, Inc. +// +// 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 types + +import ( + "net/url" + + "github.com/coreos/ignition/config/shared/errors" + "github.com/vincent-petithory/dataurl" +) + +func validateURL(s string) error { + // Empty url is valid, indicates an empty file + if s == "" { + return nil + } + u, err := url.Parse(s) + if err != nil { + return errors.ErrInvalidUrl + } + + switch u.Scheme { + case "http", "https", "oem", "tftp", "s3": + return nil + case "data": + if _, err := dataurl.DecodeString(s); err != nil { + return err + } + return nil + default: + return errors.ErrInvalidScheme + } +} diff --git a/vendor/github.com/coreos/ignition/config/v2_1/types/verification.go b/vendor/github.com/coreos/ignition/config/v2_1/types/verification.go new file mode 100644 index 00000000000..51e7d1550a3 --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v2_1/types/verification.go @@ -0,0 +1,77 @@ +// Copyright 2016 CoreOS, Inc. +// +// 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 types + +import ( + "crypto" + "encoding/hex" + "strings" + + "github.com/coreos/ignition/config/shared/errors" + "github.com/coreos/ignition/config/validate/report" +) + +// HashParts will return the sum and function (in that order) of the hash stored +// in this Verification, or an error if there is an issue during parsing. +func (v Verification) HashParts() (string, string, error) { + if v.Hash == nil { + // The hash can be nil + return "", "", nil + } + parts := strings.SplitN(*v.Hash, "-", 2) + if len(parts) != 2 { + return "", "", errors.ErrHashMalformed + } + + return parts[0], parts[1], nil +} + +func (v Verification) Validate() report.Report { + r := report.Report{} + + if v.Hash == nil { + // The hash can be nil + return r + } + + function, sum, err := v.HashParts() + if err != nil { + r.Add(report.Entry{ + Message: err.Error(), + Kind: report.EntryError, + }) + return r + } + var hash crypto.Hash + switch function { + case "sha512": + hash = crypto.SHA512 + default: + r.Add(report.Entry{ + Message: errors.ErrHashUnrecognized.Error(), + Kind: report.EntryError, + }) + return r + } + + if len(sum) != hex.EncodedLen(hash.Size()) { + r.Add(report.Entry{ + Message: errors.ErrHashWrongSize.Error(), + Kind: report.EntryError, + }) + } + + return r +} diff --git a/vendor/github.com/coreos/ignition/config/v2_2/append.go b/vendor/github.com/coreos/ignition/config/v2_2/append.go new file mode 100644 index 00000000000..cf28f40905d --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v2_2/append.go @@ -0,0 +1,76 @@ +// Copyright 2016 CoreOS, Inc. +// +// 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 v2_2 + +import ( + "reflect" + + "github.com/coreos/ignition/config/v2_2/types" +) + +// Append appends newConfig to oldConfig and returns the result. Appending one +// config to another is accomplished by iterating over every field in the +// config structure, appending slices, recursively appending structs, and +// overwriting old values with new values for all other types. +func Append(oldConfig, newConfig types.Config) types.Config { + vOld := reflect.ValueOf(oldConfig) + vNew := reflect.ValueOf(newConfig) + + vResult := appendStruct(vOld, vNew) + + return vResult.Interface().(types.Config) +} + +// appendStruct is an internal helper function to AppendConfig. Given two values +// of structures (assumed to be the same type), recursively iterate over every +// field in the struct, appending slices, recursively appending structs, and +// overwriting old values with the new for all other types. Some individual +// struct fields have alternate merge strategies, determined by the field name. +// Currently these fields are "ignition.version", which uses the old value, and +// "ignition.config" which uses the new value. +func appendStruct(vOld, vNew reflect.Value) reflect.Value { + tOld := vOld.Type() + vRes := reflect.New(tOld) + + for i := 0; i < tOld.NumField(); i++ { + vfOld := vOld.Field(i) + vfNew := vNew.Field(i) + vfRes := vRes.Elem().Field(i) + + switch tOld.Field(i).Name { + case "Version": + vfRes.Set(vfOld) + continue + case "Config": + vfRes.Set(vfNew) + continue + } + + switch vfOld.Type().Kind() { + case reflect.Struct: + vfRes.Set(appendStruct(vfOld, vfNew)) + case reflect.Slice: + vfRes.Set(reflect.AppendSlice(vfOld, vfNew)) + default: + if vfNew.Kind() == reflect.Ptr && vfNew.IsNil() { + vfRes.Set(vfOld) + } else { + vfRes.Set(vfNew) + } + } + } + + return vRes.Elem() +} diff --git a/vendor/github.com/coreos/ignition/config/v2_2/cloudinit.go b/vendor/github.com/coreos/ignition/config/v2_2/cloudinit.go new file mode 100644 index 00000000000..36a54393245 --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v2_2/cloudinit.go @@ -0,0 +1,53 @@ +// Copyright 2015 CoreOS, Inc. +// +// 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. + +// These functions are copied from github.com/coreos/coreos-cloudinit/config. + +package v2_2 + +import ( + "bytes" + "compress/gzip" + "io/ioutil" + "strings" + "unicode" +) + +func isCloudConfig(userdata []byte) bool { + header := strings.SplitN(string(decompressIfGzipped(userdata)), "\n", 2)[0] + + // Trim trailing whitespaces + header = strings.TrimRightFunc(header, unicode.IsSpace) + + return (header == "#cloud-config") +} + +func isScript(userdata []byte) bool { + header := strings.SplitN(string(decompressIfGzipped(userdata)), "\n", 2)[0] + return strings.HasPrefix(header, "#!") +} + +func decompressIfGzipped(data []byte) []byte { + if reader, err := gzip.NewReader(bytes.NewReader(data)); err == nil { + uncompressedData, err := ioutil.ReadAll(reader) + reader.Close() + if err == nil { + return uncompressedData + } else { + return data + } + } else { + return data + } +} diff --git a/vendor/github.com/coreos/ignition/config/v2_2/config.go b/vendor/github.com/coreos/ignition/config/v2_2/config.go new file mode 100644 index 00000000000..c934a9e4a38 --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v2_2/config.go @@ -0,0 +1,71 @@ +// Copyright 2015 CoreOS, Inc. +// +// 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 v2_2 + +import ( + "github.com/coreos/ignition/config/shared/errors" + "github.com/coreos/ignition/config/v2_1" + "github.com/coreos/ignition/config/v2_2/types" + "github.com/coreos/ignition/config/validate" + "github.com/coreos/ignition/config/validate/report" + + json "github.com/ajeddeloh/go-json" + "github.com/coreos/go-semver/semver" +) + +// Parse parses the raw config into a types.Config struct and generates a report of any +// errors, warnings, info, and deprecations it encountered. Unlike config.Parse, +// it does not attempt to translate the config. +func Parse(rawConfig []byte) (types.Config, report.Report, error) { + if isEmpty(rawConfig) { + return types.Config{}, report.Report{}, errors.ErrEmpty + } else if isCloudConfig(rawConfig) { + return types.Config{}, report.Report{}, errors.ErrCloudConfig + } else if isScript(rawConfig) { + return types.Config{}, report.Report{}, errors.ErrScript + } + + var err error + var config types.Config + + err = json.Unmarshal(rawConfig, &config) + + version, semverErr := semver.NewVersion(config.Ignition.Version) + + if err != nil || semverErr != nil || version.LessThan(types.MaxVersion) { + // We can fail unmarshaling if it's an older config. Attempt to parse + // it as such. + config, rpt, err := v2_1.Parse(rawConfig) + if err != nil { + return types.Config{}, rpt, err + } + return TranslateFromV2_1(config), rpt, err + } + + if *version != types.MaxVersion { + return types.Config{}, report.Report{}, errors.ErrUnknownVersion + } + + rpt := validate.ValidateConfig(rawConfig, config) + if rpt.IsFatal() { + return types.Config{}, rpt, errors.ErrInvalid + } + + return config, rpt, nil +} + +func isEmpty(userdata []byte) bool { + return len(userdata) == 0 +} diff --git a/vendor/github.com/coreos/ignition/config/v2_2/translate.go b/vendor/github.com/coreos/ignition/config/v2_2/translate.go new file mode 100644 index 00000000000..56a6b33fc94 --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/v2_2/translate.go @@ -0,0 +1,354 @@ +// Copyright 2018 CoreOS, Inc. +// +// 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 v2_2 + +import ( + "github.com/coreos/ignition/config/util" + v2_1 "github.com/coreos/ignition/config/v2_1/types" + "github.com/coreos/ignition/config/v2_2/types" +) + +// golang-- +func translateStringSliceToV2_2SSHAuthorizedKeySlice(keys []string) []types.SSHAuthorizedKey { + newKeys := make([]types.SSHAuthorizedKey, len(keys)) + for i, k := range keys { + newKeys[i] = types.SSHAuthorizedKey(k) + } + return newKeys +} + +// golang-- +func translateStringSliceToV2_2UsercreateGroupSlice(groups []string) []types.UsercreateGroup { + var newGroups []types.UsercreateGroup + for _, g := range groups { + newGroups = append(newGroups, types.UsercreateGroup(g)) + } + return newGroups +} + +func TranslateFromV2_1(old v2_1.Config) types.Config { + translateConfigReference := func(old *v2_1.ConfigReference) *types.ConfigReference { + if old == nil { + return nil + } + return &types.ConfigReference{ + Source: old.Source, + Verification: types.Verification{ + Hash: old.Verification.Hash, + }, + } + } + translateConfigReferenceSlice := func(old []v2_1.ConfigReference) []types.ConfigReference { + var res []types.ConfigReference + for _, c := range old { + res = append(res, *translateConfigReference(&c)) + } + return res + } + translateNetworkdUnitSlice := func(old []v2_1.Networkdunit) []types.Networkdunit { + var res []types.Networkdunit + for _, u := range old { + res = append(res, types.Networkdunit{ + Contents: u.Contents, + Name: u.Name, + }) + } + return res + } + translatePasswdGroupSlice := func(old []v2_1.PasswdGroup) []types.PasswdGroup { + var res []types.PasswdGroup + for _, g := range old { + res = append(res, types.PasswdGroup{ + Gid: g.Gid, + Name: g.Name, + PasswordHash: g.PasswordHash, + System: g.System, + }) + } + return res + } + translatePasswdUsercreateGroupSlice := func(old []v2_1.UsercreateGroup) []types.UsercreateGroup { + var res []types.UsercreateGroup + for _, g := range old { + res = append(res, types.UsercreateGroup(g)) + } + return res + } + translatePasswdUsercreate := func(old *v2_1.Usercreate) *types.Usercreate { + if old == nil { + return nil + } + return &types.Usercreate{ + Gecos: old.Gecos, + Groups: translatePasswdUsercreateGroupSlice(old.Groups), + HomeDir: old.HomeDir, + NoCreateHome: old.NoCreateHome, + NoLogInit: old.NoLogInit, + NoUserGroup: old.NoUserGroup, + PrimaryGroup: old.PrimaryGroup, + Shell: old.Shell, + System: old.System, + UID: old.UID, + } + } + translatePasswdUserGroupSlice := func(old []v2_1.PasswdUserGroup) []types.Group { + var res []types.Group + for _, g := range old { + res = append(res, types.Group(g)) + } + return res + } + translatePasswdSSHAuthorizedKeySlice := func(old []v2_1.SSHAuthorizedKey) []types.SSHAuthorizedKey { + res := make([]types.SSHAuthorizedKey, len(old)) + for i, k := range old { + res[i] = types.SSHAuthorizedKey(k) + } + return res + } + translatePasswdUserSlice := func(old []v2_1.PasswdUser) []types.PasswdUser { + var res []types.PasswdUser + for _, u := range old { + res = append(res, types.PasswdUser{ + Create: translatePasswdUsercreate(u.Create), + Gecos: u.Gecos, + Groups: translatePasswdUserGroupSlice(u.Groups), + HomeDir: u.HomeDir, + Name: u.Name, + NoCreateHome: u.NoCreateHome, + NoLogInit: u.NoLogInit, + NoUserGroup: u.NoUserGroup, + PasswordHash: u.PasswordHash, + PrimaryGroup: u.PrimaryGroup, + SSHAuthorizedKeys: translatePasswdSSHAuthorizedKeySlice(u.SSHAuthorizedKeys), + Shell: u.Shell, + System: u.System, + UID: u.UID, + }) + } + return res + } + translateNodeGroup := func(old v2_1.NodeGroup) *types.NodeGroup { + return &types.NodeGroup{ + ID: old.ID, + Name: old.Name, + } + } + translateNodeUser := func(old v2_1.NodeUser) *types.NodeUser { + return &types.NodeUser{ + ID: old.ID, + Name: old.Name, + } + } + translateNode := func(old v2_1.Node) types.Node { + return types.Node{ + Filesystem: old.Filesystem, + Group: translateNodeGroup(old.Group), + Path: old.Path, + User: translateNodeUser(old.User), + } + } + translateDirectorySlice := func(old []v2_1.Directory) []types.Directory { + var res []types.Directory + for _, x := range old { + res = append(res, types.Directory{ + Node: translateNode(x.Node), + DirectoryEmbedded1: types.DirectoryEmbedded1{ + Mode: util.IntToPtr(x.DirectoryEmbedded1.Mode), + }, + }) + } + return res + } + translatePartitionSlice := func(old []v2_1.Partition) []types.Partition { + var res []types.Partition + for _, x := range old { + res = append(res, types.Partition{ + GUID: x.GUID, + Label: x.Label, + Number: x.Number, + Size: x.Size, + Start: x.Start, + TypeGUID: x.TypeGUID, + }) + } + return res + } + translateDiskSlice := func(old []v2_1.Disk) []types.Disk { + var res []types.Disk + for _, x := range old { + res = append(res, types.Disk{ + Device: x.Device, + Partitions: translatePartitionSlice(x.Partitions), + WipeTable: x.WipeTable, + }) + } + return res + } + translateFileSlice := func(old []v2_1.File) []types.File { + var res []types.File + for _, x := range old { + res = append(res, types.File{ + Node: translateNode(x.Node), + FileEmbedded1: types.FileEmbedded1{ + Contents: types.FileContents{ + Compression: x.Contents.Compression, + Source: x.Contents.Source, + Verification: types.Verification{ + Hash: x.Contents.Verification.Hash, + }, + }, + Mode: util.IntToPtr(x.Mode), + }, + }) + } + return res + } + translateMountCreateOptionSlice := func(old []v2_1.CreateOption) []types.CreateOption { + var res []types.CreateOption + for _, x := range old { + res = append(res, types.CreateOption(x)) + } + return res + } + translateMountCreate := func(old *v2_1.Create) *types.Create { + if old == nil { + return nil + } + return &types.Create{ + Force: old.Force, + Options: translateMountCreateOptionSlice(old.Options), + } + } + translateMountOptionSlice := func(old []v2_1.MountOption) []types.MountOption { + var res []types.MountOption + for _, x := range old { + res = append(res, types.MountOption(x)) + } + return res + } + translateMount := func(old *v2_1.Mount) *types.Mount { + if old == nil { + return nil + } + return &types.Mount{ + Create: translateMountCreate(old.Create), + Device: old.Device, + Format: old.Format, + Label: old.Label, + Options: translateMountOptionSlice(old.Options), + UUID: old.UUID, + WipeFilesystem: old.WipeFilesystem, + } + } + translateFilesystemSlice := func(old []v2_1.Filesystem) []types.Filesystem { + var res []types.Filesystem + for _, x := range old { + res = append(res, types.Filesystem{ + Mount: translateMount(x.Mount), + Name: x.Name, + Path: x.Path, + }) + } + return res + } + translateLinkSlice := func(old []v2_1.Link) []types.Link { + var res []types.Link + for _, x := range old { + res = append(res, types.Link{ + Node: translateNode(x.Node), + LinkEmbedded1: types.LinkEmbedded1{ + Hard: x.Hard, + Target: x.Target, + }, + }) + } + return res + } + translateDeviceSlice := func(old []v2_1.Device) []types.Device { + var res []types.Device + for _, x := range old { + res = append(res, types.Device(x)) + } + return res + } + translateRaidSlice := func(old []v2_1.Raid) []types.Raid { + var res []types.Raid + for _, x := range old { + res = append(res, types.Raid{ + Devices: translateDeviceSlice(x.Devices), + Level: x.Level, + Name: x.Name, + Spares: x.Spares, + }) + } + return res + } + translateSystemdDropinSlice := func(old []v2_1.Dropin) []types.SystemdDropin { + var res []types.SystemdDropin + for _, x := range old { + res = append(res, types.SystemdDropin{ + Contents: x.Contents, + Name: x.Name, + }) + } + return res + } + translateSystemdUnitSlice := func(old []v2_1.Unit) []types.Unit { + var res []types.Unit + for _, x := range old { + res = append(res, types.Unit{ + Contents: x.Contents, + Dropins: translateSystemdDropinSlice(x.Dropins), + Enable: x.Enable, + Enabled: x.Enabled, + Mask: x.Mask, + Name: x.Name, + }) + } + return res + } + config := types.Config{ + Ignition: types.Ignition{ + Version: types.MaxVersion.String(), + Timeouts: types.Timeouts{ + HTTPResponseHeaders: old.Ignition.Timeouts.HTTPResponseHeaders, + HTTPTotal: old.Ignition.Timeouts.HTTPTotal, + }, + Config: types.IgnitionConfig{ + Replace: translateConfigReference(old.Ignition.Config.Replace), + Append: translateConfigReferenceSlice(old.Ignition.Config.Append), + }, + }, + Networkd: types.Networkd{ + Units: translateNetworkdUnitSlice(old.Networkd.Units), + }, + Passwd: types.Passwd{ + Groups: translatePasswdGroupSlice(old.Passwd.Groups), + Users: translatePasswdUserSlice(old.Passwd.Users), + }, + Storage: types.Storage{ + Directories: translateDirectorySlice(old.Storage.Directories), + Disks: translateDiskSlice(old.Storage.Disks), + Files: translateFileSlice(old.Storage.Files), + Filesystems: translateFilesystemSlice(old.Storage.Filesystems), + Links: translateLinkSlice(old.Storage.Links), + Raid: translateRaidSlice(old.Storage.Raid), + }, + Systemd: types.Systemd{ + Units: translateSystemdUnitSlice(old.Systemd.Units), + }, + } + return config +} diff --git a/vendor/github.com/coreos/ignition/config/validate/astjson/node.go b/vendor/github.com/coreos/ignition/config/validate/astjson/node.go new file mode 100644 index 00000000000..6735fa0ab3f --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/validate/astjson/node.go @@ -0,0 +1,73 @@ +// Copyright 2016 CoreOS, Inc. +// +// 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 astjson + +import ( + "io" + + json "github.com/ajeddeloh/go-json" + "github.com/coreos/ignition/config/validate/astnode" + "go4.org/errorutil" +) + +type JsonNode json.Node + +func FromJsonRoot(n json.Node) JsonNode { + return JsonNode(n) +} + +func (n JsonNode) ValueLineCol(source io.ReadSeeker) (int, int, string) { + return posFromOffset(n.End, source) +} + +func (n JsonNode) KeyLineCol(source io.ReadSeeker) (int, int, string) { + return posFromOffset(n.KeyEnd, source) +} + +func (n JsonNode) LiteralValue() interface{} { + return n.Value +} + +func (n JsonNode) SliceChild(index int) (astnode.AstNode, bool) { + if slice, ok := n.Value.([]json.Node); ok { + return JsonNode(slice[index]), true + } + return JsonNode{}, false +} + +func (n JsonNode) KeyValueMap() (map[string]astnode.AstNode, bool) { + if kvmap, ok := n.Value.(map[string]json.Node); ok { + newKvmap := map[string]astnode.AstNode{} + for k, v := range kvmap { + newKvmap[k] = JsonNode(v) + } + return newKvmap, true + } + return nil, false +} + +func (n JsonNode) Tag() string { + return "json" +} + +// wrapper for errorutil that handles missing sources sanely and resets the reader afterwards +func posFromOffset(offset int, source io.ReadSeeker) (int, int, string) { + if source == nil { + return 0, 0, "" + } + line, col, highlight := errorutil.HighlightBytePosition(source, int64(offset)) + source.Seek(0, 0) // Reset the reader to the start so the next call isn't relative to this position + return line, col, highlight +} diff --git a/vendor/github.com/coreos/ignition/config/validate/astnode/astnode.go b/vendor/github.com/coreos/ignition/config/validate/astnode/astnode.go new file mode 100644 index 00000000000..d1c1d9c2420 --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/validate/astnode/astnode.go @@ -0,0 +1,45 @@ +// Copyright 2017 CoreOS, Inc. +// +// 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 astnode + +import ( + "io" +) + +// AstNode abstracts the differences between yaml and json nodes, providing a +// common interface +type AstNode interface { + // ValueLineCol returns the line, column, and highlight string of the value of + // this node in the source. + ValueLineCol(source io.ReadSeeker) (int, int, string) + + // KeyLineCol returns the line, column, and highlight string of the key for the + // value of this node in the source. + KeyLineCol(source io.ReadSeeker) (int, int, string) + + // LiteralValue returns the value of this node. + LiteralValue() interface{} + + // SliceChild returns the child node at the index specified. If this node is not + // a slice node, an empty AstNode and false is returned. + SliceChild(index int) (AstNode, bool) + + // KeyValueMap returns a map of keys and values. If this node is not a mapping + // node, nil and false are returned. + KeyValueMap() (map[string]AstNode, bool) + + // Tag returns the struct tag used in the config structure used to unmarshal. + Tag() string +} diff --git a/vendor/github.com/coreos/ignition/config/validate/validate.go b/vendor/github.com/coreos/ignition/config/validate/validate.go new file mode 100644 index 00000000000..12f9bf205fa --- /dev/null +++ b/vendor/github.com/coreos/ignition/config/validate/validate.go @@ -0,0 +1,242 @@ +// Copyright 2015 CoreOS, Inc. +// +// 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 validate + +import ( + "bytes" + "fmt" + "io" + "reflect" + "strings" + + json "github.com/ajeddeloh/go-json" + "github.com/coreos/ignition/config/validate/astjson" + "github.com/coreos/ignition/config/validate/astnode" + "github.com/coreos/ignition/config/validate/report" +) + +type validator interface { + Validate() report.Report +} + +// ValidateConfig validates a raw config object into a given config version +func ValidateConfig(rawConfig []byte, config interface{}) report.Report { + // Unmarshal again to a json.Node to get offset information for building a report + var ast json.Node + var r report.Report + configValue := reflect.ValueOf(config) + if err := json.Unmarshal(rawConfig, &ast); err != nil { + r.Add(report.Entry{ + Kind: report.EntryWarning, + Message: "Ignition could not unmarshal your config for reporting line numbers. This should never happen. Please file a bug.", + }) + r.Merge(ValidateWithoutSource(configValue)) + } else { + r.Merge(Validate(configValue, astjson.FromJsonRoot(ast), bytes.NewReader(rawConfig), true)) + } + return r +} + +// Validate walks down a struct tree calling Validate on every node that implements it, building +// A report of all the errors, warnings, info, and deprecations it encounters. If checkUnusedKeys +// is true, Validate will generate warnings for unused keys in the ast, otherwise it will not. +func Validate(vObj reflect.Value, ast astnode.AstNode, source io.ReadSeeker, checkUnusedKeys bool) (r report.Report) { + if !vObj.IsValid() { + return + } + + line, col, highlight := 0, 0, "" + if ast != nil { + line, col, highlight = ast.ValueLineCol(source) + } + + // See if we A) can call Validate on vObj, and B) should call Validate. Validate should NOT be called + // when vObj is nil, as it will panic or when vObj is a pointer to a value with Validate implemented with a + // value receiver. This is to prevent Validate being called twice, as otherwise it would be called on the + // pointer version (due to go's automatic deferencing) and once when the pointer is deferenced below. The only + // time Validate should be called on a pointer is when the function is implemented with a pointer reciever. + if obj, ok := vObj.Interface().(validator); ok && + ((vObj.Kind() != reflect.Ptr) || + (!vObj.IsNil() && !vObj.Elem().Type().Implements(reflect.TypeOf((*validator)(nil)).Elem()))) { + sub_r := obj.Validate() + sub_r.AddPosition(line, col, highlight) + r.Merge(sub_r) + + // Dont recurse on invalid inner nodes, it mostly leads to bogus messages + if sub_r.IsFatal() { + return + } + } + + switch vObj.Kind() { + case reflect.Ptr: + sub_report := Validate(vObj.Elem(), ast, source, checkUnusedKeys) + sub_report.AddPosition(line, col, "") + r.Merge(sub_report) + case reflect.Struct: + sub_report := validateStruct(vObj, ast, source, checkUnusedKeys) + sub_report.AddPosition(line, col, "") + r.Merge(sub_report) + case reflect.Slice: + for i := 0; i < vObj.Len(); i++ { + sub_node := ast + if ast != nil { + if n, ok := ast.SliceChild(i); ok { + sub_node = n + } + } + sub_report := Validate(vObj.Index(i), sub_node, source, checkUnusedKeys) + sub_report.AddPosition(line, col, "") + r.Merge(sub_report) + } + } + return +} + +func ValidateWithoutSource(cfg reflect.Value) (report report.Report) { + return Validate(cfg, nil, nil, false) +} + +type field struct { + Type reflect.StructField + Value reflect.Value +} + +// getFields returns a field of all the fields in the struct, including the fields of +// embedded structs and structs inside interface{}'s +func getFields(vObj reflect.Value) []field { + if vObj.Kind() != reflect.Struct { + return nil + } + ret := []field{} + for i := 0; i < vObj.Type().NumField(); i++ { + if vObj.Type().Field(i).Anonymous { + // in the case of an embedded type that is an alias to interface, extract the + // real type contained by the interface + realObj := reflect.ValueOf(vObj.Field(i).Interface()) + ret = append(ret, getFields(realObj)...) + } else { + ret = append(ret, field{Type: vObj.Type().Field(i), Value: vObj.Field(i)}) + } + } + return ret +} + +func validateStruct(vObj reflect.Value, ast astnode.AstNode, source io.ReadSeeker, checkUnusedKeys bool) report.Report { + r := report.Report{} + + // isFromObject will be true if this struct was unmarshalled from a JSON object. + keys, isFromObject := map[string]astnode.AstNode{}, false + if ast != nil { + keys, isFromObject = ast.KeyValueMap() + } + + // Maintain a set of key's that have been used. + usedKeys := map[string]struct{}{} + + // Maintain a list of all the tags in the struct for fuzzy matching later. + tags := []string{} + + for _, f := range getFields(vObj) { + // Default to nil astnode.AstNode if the field's corrosponding node cannot be found. + var sub_node astnode.AstNode + // Default to passing a nil source if the field's corrosponding node cannot be found. + // This ensures the line numbers reported from all sub-structs are 0 and will be changed by AddPosition + var src io.ReadSeeker + + // Try to determine the json.Node that corrosponds with the struct field + if isFromObject { + tag := strings.SplitN(f.Type.Tag.Get(ast.Tag()), ",", 2)[0] + // Save the tag so we have a list of all the tags in the struct + tags = append(tags, tag) + // mark that this key was used + usedKeys[tag] = struct{}{} + + if sub, ok := keys[tag]; ok { + // Found it + sub_node = sub + src = source + } + } + + // Default to deepest node if the node's type isn't an object, + // such as when a json string actually unmarshal to structs (like with version) + line, col := 0, 0 + highlight := "" + if ast != nil { + line, col, highlight = ast.ValueLineCol(src) + } + + // If there's a Validate func for the given field, call it + funct := vObj.MethodByName("Validate" + f.Type.Name) + if funct.IsValid() { + if sub_node != nil { + // if sub_node is non-nil, we can get better line/col info + line, col, highlight = sub_node.ValueLineCol(src) + } + res := funct.Call(nil) + sub_report := res[0].Interface().(report.Report) + sub_report.AddPosition(line, col, highlight) + r.Merge(sub_report) + } + + sub_report := Validate(f.Value, sub_node, src, checkUnusedKeys) + sub_report.AddPosition(line, col, highlight) + r.Merge(sub_report) + } + if !isFromObject || !checkUnusedKeys { + // If this struct was not unmarshalled from a JSON object, there cannot be unused keys. + return r + } + + for k, v := range keys { + if _, hasKey := usedKeys[k]; hasKey { + continue + } + line, col, highlight := v.KeyLineCol(source) + typo := similar(k, tags) + + r.Add(report.Entry{ + Kind: report.EntryWarning, + Message: fmt.Sprintf("Config has unrecognized key: %s", k), + Line: line, + Column: col, + Highlight: highlight, + }) + + if typo != "" { + r.Add(report.Entry{ + Kind: report.EntryInfo, + Message: fmt.Sprintf("Did you mean %s instead of %s", typo, k), + Line: line, + Column: col, + Highlight: highlight, + }) + } + } + + return r +} + +// similar returns a string in candidates that is similar to str. Currently it just does case +// insensitive comparison, but it should be updated to use levenstein distances to catch typos +func similar(str string, candidates []string) string { + for _, candidate := range candidates { + if strings.EqualFold(str, candidate) { + return candidate + } + } + return "" +} diff --git a/vendor/github.com/openshift/machine-config-operator/LICENSE b/vendor/github.com/openshift/machine-config-operator/LICENSE new file mode 100644 index 00000000000..c4ea8b6f9d8 --- /dev/null +++ b/vendor/github.com/openshift/machine-config-operator/LICENSE @@ -0,0 +1,191 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2014 Red Hat, Inc. + + 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. diff --git a/vendor/github.com/openshift/machine-config-operator/pkg/apis/apis.go b/vendor/github.com/openshift/machine-config-operator/pkg/apis/apis.go new file mode 100644 index 00000000000..87a14e6e927 --- /dev/null +++ b/vendor/github.com/openshift/machine-config-operator/pkg/apis/apis.go @@ -0,0 +1,4 @@ +package apis + +// GroupName defines the API group for machineconfiguration. +const GroupName = "machineconfiguration.openshift.io" diff --git a/vendor/github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1/doc.go b/vendor/github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1/doc.go new file mode 100644 index 00000000000..6015c363ae0 --- /dev/null +++ b/vendor/github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1/doc.go @@ -0,0 +1,4 @@ +// +k8s:deepcopy-gen=package + +// Package v1 is the v1 version of the API. +package v1 diff --git a/vendor/github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1/helpers.go b/vendor/github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1/helpers.go new file mode 100644 index 00000000000..bb08fe9721a --- /dev/null +++ b/vendor/github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1/helpers.go @@ -0,0 +1,87 @@ +package v1 + +import ( + "sort" + + ignv2_2 "github.com/coreos/ignition/config/v2_2" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// MergeMachineConfigs combines multiple machineconfig objects into one object. +// It sorts all the configs in increasing order of their name. +// It uses the Ign config from first object as base and appends all the rest. +// It only uses the OSImageURL from first object and ignores it from rest. +func MergeMachineConfigs(configs []*MachineConfig) *MachineConfig { + if len(configs) == 0 { + return nil + } + sort.Slice(configs, func(i, j int) bool { return configs[i].Name < configs[j].Name }) + + outOSImageURL := configs[0].Spec.OSImageURL + outIgn := configs[0].Spec.Config + for idx := 1; idx < len(configs); idx++ { + outIgn = ignv2_2.Append(outIgn, configs[idx].Spec.Config) + } + + return &MachineConfig{ + Spec: MachineConfigSpec{ + OSImageURL: outOSImageURL, + Config: outIgn, + }, + } +} + +// NewMachineConfigPoolCondition creates a new MachineConfigPool condition. +func NewMachineConfigPoolCondition(condType MachineConfigPoolConditionType, status corev1.ConditionStatus, reason, message string) *MachineConfigPoolCondition { + return &MachineConfigPoolCondition{ + Type: condType, + Status: status, + LastTransitionTime: metav1.Now(), + Reason: reason, + Message: message, + } +} + +// GetMachineConfigPoolCondition returns the condition with the provided type. +func GetMachineConfigPoolCondition(status MachineConfigPoolStatus, condType MachineConfigPoolConditionType) *MachineConfigPoolCondition { + for i := range status.Conditions { + c := status.Conditions[i] + if c.Type == condType { + return &c + } + } + return nil +} + +// SetMachineConfigPoolCondition updates the MachineConfigPool to include the provided condition. If the condition that +// we are about to add already exists and has the same status and reason then we are not going to update. +func SetMachineConfigPoolCondition(status *MachineConfigPoolStatus, condition MachineConfigPoolCondition) { + currentCond := GetMachineConfigPoolCondition(*status, condition.Type) + if currentCond != nil && currentCond.Status == condition.Status && currentCond.Reason == condition.Reason { + return + } + // Do not update lastTransitionTime if the status of the condition doesn't change. + if currentCond != nil && currentCond.Status == condition.Status { + condition.LastTransitionTime = currentCond.LastTransitionTime + } + newConditions := filterOutCondition(status.Conditions, condition.Type) + status.Conditions = append(newConditions, condition) +} + +// RemoveMachineConfigPoolCondition removes the MachineConfigPool condition with the provided type. +func RemoveMachineConfigPoolCondition(status *MachineConfigPoolStatus, condType MachineConfigPoolConditionType) { + status.Conditions = filterOutCondition(status.Conditions, condType) +} + +// filterOutCondition returns a new slice of MachineConfigPool conditions without conditions with the provided type. +func filterOutCondition(conditions []MachineConfigPoolCondition, condType MachineConfigPoolConditionType) []MachineConfigPoolCondition { + var newConditions []MachineConfigPoolCondition + for _, c := range conditions { + if c.Type == condType { + continue + } + newConditions = append(newConditions, c) + } + return newConditions +} diff --git a/vendor/github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1/machineconfig.deepcopy.go b/vendor/github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1/machineconfig.deepcopy.go new file mode 100644 index 00000000000..fa05fa6255b --- /dev/null +++ b/vendor/github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1/machineconfig.deepcopy.go @@ -0,0 +1,57 @@ +package v1 + +import ( + ignv2_2 "github.com/coreos/ignition/config/v2_2" + ignv2_2types "github.com/coreos/ignition/config/v2_2/types" + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto copying the receiver, writing into out. in must be non-nil. +func (in *MachineConfig) DeepCopyInto(out *MachineConfig) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + return +} + +// DeepCopy copying the receiver, creating a new MachineConfig. +func (in *MachineConfig) DeepCopy() *MachineConfig { + if in == nil { + return nil + } + out := new(MachineConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject copying the receiver, creating a new runtime.Object. +func (in *MachineConfig) DeepCopyObject() runtime.Object { + return in.DeepCopy() +} + +// DeepCopyInto copying the receiver, writing into out. in must be non-nil. +func (in *MachineConfigSpec) DeepCopyInto(out *MachineConfigSpec) { + *out = *in + out.Config = deepCopyIgnConfig(in.Config) + return +} + +func deepCopyIgnConfig(in ignv2_2types.Config) ignv2_2types.Config { + var out ignv2_2types.Config + + // https://github.com/coreos/ignition/blob/d19b2021cf397de7c31774c13805bbc3aa655646/config/v2_2/append.go#L41 + out.Ignition.Version = in.Ignition.Version + + return ignv2_2.Append(out, in) +} + +// DeepCopy copying the receiver, creating a new MachineConfigSpec. +func (in *MachineConfigSpec) DeepCopy() *MachineConfigSpec { + if in == nil { + return nil + } + out := new(MachineConfigSpec) + in.DeepCopyInto(out) + return out +} diff --git a/vendor/github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1/register.go b/vendor/github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1/register.go new file mode 100644 index 00000000000..c911c24e89e --- /dev/null +++ b/vendor/github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1/register.go @@ -0,0 +1,53 @@ +package v1 + +import ( + "github.com/openshift/machine-config-operator/pkg/apis" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +const ( + // GroupName defines the API group for machineconfigpools. + GroupName = apis.GroupName +) + +// SchemeGroupVersion is group version used to register these objects +var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1"} + +// Resource takes an unqualified resource and returns a Group qualified GroupResource +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} + +var ( + // SchemeBuilder is the scheme builder for MachineConfigPools + SchemeBuilder runtime.SchemeBuilder + // localSchemeBuilder and AddToScheme will stay in k8s.io/kubernetes. + localSchemeBuilder = &SchemeBuilder + // AddToScheme is the function alias for AddtoScheme + AddToScheme = localSchemeBuilder.AddToScheme +) + +func init() { + // We only register manually written functions here. The registration of the + // generated functions takes place in the generated files. The separation + // makes the code compile even when the generated files are missing. + localSchemeBuilder.Register(addKnownTypes) +} + +// Adds the list of known types to api.Scheme. +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(SchemeGroupVersion, + &MCOConfig{}, + &ControllerConfig{}, + &ControllerConfigList{}, + &MachineConfig{}, + &MachineConfigList{}, + &MachineConfigPool{}, + &MachineConfigPoolList{}, + ) + + metav1.AddToGroupVersion(scheme, SchemeGroupVersion) + return nil +} diff --git a/vendor/github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1/types.go b/vendor/github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1/types.go new file mode 100644 index 00000000000..efed650e1ac --- /dev/null +++ b/vendor/github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1/types.go @@ -0,0 +1,337 @@ +package v1 + +import ( + ignv2_2types "github.com/coreos/ignition/config/v2_2/types" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" +) + +// CustomResourceDefinition for MCOConfig +// apiVersion: apiextensions.k8s.io/v1beta1 +// kind: CustomResourceDefinition +// metadata: +// # name must match the spec fields below, and be in the form: . +// name: mcoconfigs.machineconfiguration.openshift.io +// spec: +// # group name to use for REST API: /apis// +// group: machineconfiguration.openshift.io +// # list of versions supported by this CustomResourceDefinition +// versions: +// - name: v1 +// # Each version can be enabled/disabled by Served flag. +// served: true +// # One and only one version must be marked as the storage version. +// storage: true +// # either Namespaced or Cluster +// scope: Namespaced +// names: +// # plural name to be used in the URL: /apis/// +// plural: mcoconfigs +// # singular name to be used as an alias on the CLI and for display +// singular: mcoconfig +// # kind is normally the CamelCased singular type. Your resource manifests use this. +// kind: MCOConfig +// # shortNames allow shorter string to match your resource on the CLI +// shortNames: +// + +// +genclient +// +genclient:noStatus +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// MCOConfig describes configuration for MachineConfigOperator. +type MCOConfig struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec MCOConfigSpec `json:"spec"` +} + +// MCOConfigSpec is the spec for MCOConfig resource. +type MCOConfigSpec struct { + ClusterDNSIP string `json:"clusterDNSIP"` + CloudProviderConfig string `json:"cloudProviderConfig"` + ClusterName string `json:"clusterName"` + + // The openshift platform, e.g. "libvirt" or "aws" + Platform string `json:"platform"` + + BaseDomain string `json:"baseDomain"` + + // Size of the initial etcd cluster. + EtcdInitialCount int `json:"etcdInitialCount"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// MCOConfigList is a list of MCOConfig resources +type MCOConfigList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata"` + + Items []MCOConfig `json:"items"` +} + +// CustomResourceDefinition for ControllerConfig +// apiVersion: apiextensions.k8s.io/v1beta1 +// kind: CustomResourceDefinition +// metadata: +// # name must match the spec fields below, and be in the form: . +// name: controllerconfigs.machineconfiguration.openshift.io +// spec: +// # group name to use for REST API: /apis// +// group: machineconfiguration.openshift.io +// # list of versions supported by this CustomResourceDefinition +// versions: +// - name: v1 +// # Each version can be enabled/disabled by Served flag. +// served: true +// # One and only one version must be marked as the storage version. +// storage: true +// # either Namespaced or Cluster +// scope: Namespaced +// names: +// # plural name to be used in the URL: /apis/// +// plural: controllerconfigs +// # singular name to be used as an alias on the CLI and for display +// singular: controllerconfig +// # kind is normally the CamelCased singular type. Your resource manifests use this. +// kind: ControllerConfig +// # shortNames allow shorter string to match your resource on the CLI +// shortNames: +// + +// +genclient +// +genclient:noStatus +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// ControllerConfig describes configuration for MachineConfigController. +type ControllerConfig struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec ControllerConfigSpec `json:"spec"` +} + +// ControllerConfigSpec is the spec for ControllerConfig resource. +type ControllerConfigSpec struct { + ClusterDNSIP string `json:"clusterDNSIP"` + CloudProviderConfig string `json:"cloudProviderConfig"` + ClusterName string `json:"clusterName"` + + // The openshift platform, e.g. "libvirt" or "aws" + Platform string `json:"platform"` + + BaseDomain string `json:"baseDomain"` + + // Size of the initial etcd cluster. + EtcdInitialCount int `json:"etcdInitialCount"` + + // CAs + EtcdCAData []byte `json:"etcdCAData"` + RootCAData []byte `json:"rootCAData"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// ControllerConfigList is a list of ControllerConfig resources +type ControllerConfigList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata"` + + Items []ControllerConfig `json:"items"` +} + +// CustomResourceDefinition for MachineConfig +// apiVersion: apiextensions.k8s.io/v1beta1 +// kind: CustomResourceDefinition +// metadata: +// # name must match the spec fields below, and be in the form: . +// name: machineconfigs.machineconfiguration.openshift.io +// spec: +// # group name to use for REST API: /apis// +// group: machineconfiguration.openshift.io +// # list of versions supported by this CustomResourceDefinition +// versions: +// - name: v1 +// # Each version can be enabled/disabled by Served flag. +// served: true +// # One and only one version must be marked as the storage version. +// storage: true +// # either Namespaced or Cluster +// scope: Cluster +// names: +// # plural name to be used in the URL: /apis/// +// plural: machineconfigs +// # singular name to be used as an alias on the CLI and for display +// singular: machineconfig +// # kind is normally the CamelCased singular type. Your resource manifests use this. +// kind: MachineConfig +// # shortNames allow shorter string to match your resource on the CLI +// shortNames: +// - mc + +// +genclient +// +genclient:noStatus +// +genclient:nonNamespaced +// +k8s:deepcopy-gen=false + +// MachineConfig defines the configuration for a machine +type MachineConfig struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec MachineConfigSpec `json:"spec"` +} + +// MachineConfigSpec is the for MachineConfig +type MachineConfigSpec struct { + // OSImageURL specifies the remote location that will be used to + // fetch the OS. + OSImageURL string `json:"osImageURL"` + // Config is a Ignition Config object. + Config ignv2_2types.Config `json:"config"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// MachineConfigList is a list of MachineConfig resources +type MachineConfigList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata"` + + Items []MachineConfig `json:"items"` +} + +// CustomResourceDefinition for MachineConfigPool +// apiVersion: apiextensions.k8s.io/v1beta1 +// kind: CustomResourceDefinition +// metadata: +// # name must match the spec fields below, and be in the form: . +// name: machineconfigpools.machineconfiguration.openshift.io +// spec: +// # group name to use for REST API: /apis// +// group: machineconfiguration.openshift.io +// # list of versions supported by this CustomResourceDefinition +// versions: +// - name: v1 +// # Each version can be enabled/disabled by Served flag. +// served: true +// # One and only one version must be marked as the storage version. +// storage: true +// # either Namespaced or Cluster +// scope: Cluster +// names: +// # plural name to be used in the URL: /apis/// +// plural: machineconfigpools +// # singular name to be used as an alias on the CLI and for display +// singular: machineconfigpool +// # kind is normally the CamelCased singular type. Your resource manifests use this. +// kind: MachineConfigPool +// # shortNames allow shorter string to match your resource on the CLI +// shortNames: +// + +// +genclient +// +genclient:nonNamespaced +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// MachineConfigPool describes a pool of MachineConfigs. +type MachineConfigPool struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec MachineConfigPoolSpec `json:"spec"` + Status MachineConfigPoolStatus `json:"status"` +} + +// MachineConfigPoolSpec is the spec for MachineConfigPool resource. +type MachineConfigPoolSpec struct { + // Label selector for MachineConfigs. + // Refer https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ on how label and selectors work. + MachineConfigSelector *metav1.LabelSelector `json:"machineConfigSelector,omitempty"` + + // Label selector for Machines. + MachineSelector *metav1.LabelSelector `json:"machineSelector,omitempty"` + + // If true, changes to this machine pool should be stopped. + // This includes generating new desiredMachineConfig and update of machines. + Paused bool `json:"paused"` + + // MaxUnavailable specifies the percentage or constant number of machines that can be updating at any given time. + // default is 1. + MaxUnavailable *intstr.IntOrString `json:"maxUnavailable"` +} + +// MachineConfigPoolStatus is the status for MachineConfigPool resource. +type MachineConfigPoolStatus struct { + // The generation observed by the controller. + ObservedGeneration int64 `json:"observedGeneration,omitempty"` + + // The current MachineConfig object for the machine pool. + CurrentMachineConfig string `json:"currentMachineConfig"` + + // Total number of machines in the machine pool. + MachineCount int32 `json:"machineCount"` + + // Total number of machines targeted by the pool that have the CurrentMachineConfig as their config. + UpdatedMachineCount int32 `json:"updatedMachineCount"` + + // Total number of ready machines targeted by the pool. + ReadyMachineCount int32 `json:"readyMachineCount"` + + // Total number of unavailable (non-ready) machines targeted by the pool. + // A node is marked unavailable if it is in updating state or NodeReady condition is false. + UnavailableMachineCount int32 `json:"unavailableMachineCount"` + + // Represents the latest available observations of current state. + Conditions []MachineConfigPoolCondition `json:"conditions"` +} + +// MachineConfigPoolCondition contains condition information for an MachineConfigPool. +type MachineConfigPoolCondition struct { + // Type of the condition, currently ('Done', 'Updating', 'Failed'). + Type MachineConfigPoolConditionType `json:"type"` + + // Status of the condition, one of ('True', 'False', 'Unknown'). + Status corev1.ConditionStatus `json:"status"` + + // LastTransitionTime is the timestamp corresponding to the last status + // change of this condition. + LastTransitionTime metav1.Time `json:"lastTransitionTime"` + + // Reason is a brief machine readable explanation for the condition's last + // transition. + Reason string `json:"reason"` + + // Message is a human readable description of the details of the last + // transition, complementing reason. + Message string `json:"message"` +} + +// MachineConfigPoolConditionType valid conditions of a machineconfigpool +type MachineConfigPoolConditionType string + +const ( + // MachineConfigPoolUpdated means machineconfigpool is updated completely. + // When the all the machines in the pool are updated to the correct machine config. + MachineConfigPoolUpdated MachineConfigPoolConditionType = "Updated" + // MachineConfigPoolUpdating means machineconfigpool is updating. + // When at least one of machine is not either not updated or is in the process of updating + // to the desired machine config. + MachineConfigPoolUpdating MachineConfigPoolConditionType = "Updating" + // MachineConfigPoolDegraded means the update for one of the machine is not progressing + // due to an error + MachineConfigPoolDegraded MachineConfigPoolConditionType = "Degraded" +) + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// MachineConfigPoolList is a list of MachineConfigPool resources +type MachineConfigPoolList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata"` + + Items []MachineConfigPool `json:"items"` +} diff --git a/vendor/github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1/zz_generated.deepcopy.go b/vendor/github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1/zz_generated.deepcopy.go new file mode 100644 index 00000000000..233a630acc1 --- /dev/null +++ b/vendor/github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1/zz_generated.deepcopy.go @@ -0,0 +1,338 @@ +// +build !ignore_autogenerated + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package v1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + intstr "k8s.io/apimachinery/pkg/util/intstr" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ControllerConfig) DeepCopyInto(out *ControllerConfig) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ControllerConfig. +func (in *ControllerConfig) DeepCopy() *ControllerConfig { + if in == nil { + return nil + } + out := new(ControllerConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ControllerConfig) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ControllerConfigList) DeepCopyInto(out *ControllerConfigList) { + *out = *in + out.TypeMeta = in.TypeMeta + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ControllerConfig, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ControllerConfigList. +func (in *ControllerConfigList) DeepCopy() *ControllerConfigList { + if in == nil { + return nil + } + out := new(ControllerConfigList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ControllerConfigList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ControllerConfigSpec) DeepCopyInto(out *ControllerConfigSpec) { + *out = *in + if in.EtcdCAData != nil { + in, out := &in.EtcdCAData, &out.EtcdCAData + *out = make([]byte, len(*in)) + copy(*out, *in) + } + if in.RootCAData != nil { + in, out := &in.RootCAData, &out.RootCAData + *out = make([]byte, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ControllerConfigSpec. +func (in *ControllerConfigSpec) DeepCopy() *ControllerConfigSpec { + if in == nil { + return nil + } + out := new(ControllerConfigSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MCOConfig) DeepCopyInto(out *MCOConfig) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MCOConfig. +func (in *MCOConfig) DeepCopy() *MCOConfig { + if in == nil { + return nil + } + out := new(MCOConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *MCOConfig) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MCOConfigList) DeepCopyInto(out *MCOConfigList) { + *out = *in + out.TypeMeta = in.TypeMeta + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]MCOConfig, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MCOConfigList. +func (in *MCOConfigList) DeepCopy() *MCOConfigList { + if in == nil { + return nil + } + out := new(MCOConfigList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *MCOConfigList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MCOConfigSpec) DeepCopyInto(out *MCOConfigSpec) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MCOConfigSpec. +func (in *MCOConfigSpec) DeepCopy() *MCOConfigSpec { + if in == nil { + return nil + } + out := new(MCOConfigSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MachineConfigList) DeepCopyInto(out *MachineConfigList) { + *out = *in + out.TypeMeta = in.TypeMeta + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]MachineConfig, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MachineConfigList. +func (in *MachineConfigList) DeepCopy() *MachineConfigList { + if in == nil { + return nil + } + out := new(MachineConfigList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *MachineConfigList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MachineConfigPool) DeepCopyInto(out *MachineConfigPool) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MachineConfigPool. +func (in *MachineConfigPool) DeepCopy() *MachineConfigPool { + if in == nil { + return nil + } + out := new(MachineConfigPool) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *MachineConfigPool) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MachineConfigPoolCondition) DeepCopyInto(out *MachineConfigPoolCondition) { + *out = *in + in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MachineConfigPoolCondition. +func (in *MachineConfigPoolCondition) DeepCopy() *MachineConfigPoolCondition { + if in == nil { + return nil + } + out := new(MachineConfigPoolCondition) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MachineConfigPoolList) DeepCopyInto(out *MachineConfigPoolList) { + *out = *in + out.TypeMeta = in.TypeMeta + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]MachineConfigPool, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MachineConfigPoolList. +func (in *MachineConfigPoolList) DeepCopy() *MachineConfigPoolList { + if in == nil { + return nil + } + out := new(MachineConfigPoolList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *MachineConfigPoolList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MachineConfigPoolSpec) DeepCopyInto(out *MachineConfigPoolSpec) { + *out = *in + if in.MachineConfigSelector != nil { + in, out := &in.MachineConfigSelector, &out.MachineConfigSelector + *out = new(metav1.LabelSelector) + (*in).DeepCopyInto(*out) + } + if in.MachineSelector != nil { + in, out := &in.MachineSelector, &out.MachineSelector + *out = new(metav1.LabelSelector) + (*in).DeepCopyInto(*out) + } + if in.MaxUnavailable != nil { + in, out := &in.MaxUnavailable, &out.MaxUnavailable + *out = new(intstr.IntOrString) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MachineConfigPoolSpec. +func (in *MachineConfigPoolSpec) DeepCopy() *MachineConfigPoolSpec { + if in == nil { + return nil + } + out := new(MachineConfigPoolSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MachineConfigPoolStatus) DeepCopyInto(out *MachineConfigPoolStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]MachineConfigPoolCondition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MachineConfigPoolStatus. +func (in *MachineConfigPoolStatus) DeepCopy() *MachineConfigPoolStatus { + if in == nil { + return nil + } + out := new(MachineConfigPoolStatus) + in.DeepCopyInto(out) + return out +} From 8683b8ed391dd9f3e1418fe14794e95cada9b115 Mon Sep 17 00:00:00 2001 From: Abhinav Dahiya Date: Mon, 25 Mar 2019 14:45:26 -0700 Subject: [PATCH 2/5] asset/machines: add manifests for MachineConfig Provide the means by which a machines asset can add MachineConfig manifests. This is needed so that the ignition configs can be supplemented for machine pools for example have hyperthreading disabled, include authorized_keys for user. --- pkg/asset/machines/machineconfig/manifest.go | 63 ++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 pkg/asset/machines/machineconfig/manifest.go diff --git a/pkg/asset/machines/machineconfig/manifest.go b/pkg/asset/machines/machineconfig/manifest.go new file mode 100644 index 00000000000..9b1a4cb29ff --- /dev/null +++ b/pkg/asset/machines/machineconfig/manifest.go @@ -0,0 +1,63 @@ +package machineconfig + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/ghodss/yaml" + + mcfgv1 "github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1" + + "github.com/openshift/installer/pkg/asset" +) + +const ( + machineConfigFileName = "99_openshift-machineconfig_%s.yaml" +) + +var ( + machineConfigFileNamePattern = fmt.Sprintf(machineConfigFileName, "*") +) + +// Manifests creates manifest files containing the MachineConfigs. +func Manifests(configs []*mcfgv1.MachineConfig, role, directory string) ([]*asset.File, error) { + data := []byte{} + for _, c := range configs { + if c == nil { + continue + } + configData, err := yaml.Marshal(c) + if err != nil { + return nil, err + } + data = append(data, []byte("---\n")...) + data = append(data, configData...) + } + if len(data) == 0 { + return nil, nil + } + return []*asset.File{ + { + Filename: filepath.Join(directory, fmt.Sprintf(machineConfigFileName, role)), + Data: data, + }, + }, nil +} + +// IsManifest tests whether the specified filename is a MachineConfig manifest. +func IsManifest(role, filename string) bool { + return fmt.Sprintf(machineConfigFileName, role) == filename +} + +// Load loads the MachineConfig manifests. +func Load(f asset.FileFetcher, role, directory string) ([]*asset.File, error) { + file, err := f.FetchByName(filepath.Join(directory, fmt.Sprintf(machineConfigFileName, role))) + if err != nil { + if os.IsNotExist(err) { + return nil, nil + } + return nil, err + } + return []*asset.File{file}, nil +} From f2eacf323ab6894827683d970f5de37983873f84 Mon Sep 17 00:00:00 2001 From: Abhinav Dahiya Date: Mon, 25 Mar 2019 14:55:11 -0700 Subject: [PATCH 3/5] asset/machines/master: allow adding MachineConfigs for control-plane machinepool This sets up the master machine asset to allow adding MachineConfigs. A list of machine configs can be added to master machines that will be created alongside the user-data and machine objects. --- pkg/asset/machines/master.go | 103 +++++++++++++++++++++---------- pkg/asset/manifests/openshift.go | 13 +--- 2 files changed, 71 insertions(+), 45 deletions(-) diff --git a/pkg/asset/machines/master.go b/pkg/asset/machines/master.go index a3147b8f73e..798cb2e9edd 100644 --- a/pkg/asset/machines/master.go +++ b/pkg/asset/machines/master.go @@ -14,6 +14,7 @@ import ( "github.com/openshift/installer/pkg/asset/installconfig" "github.com/openshift/installer/pkg/asset/machines/aws" "github.com/openshift/installer/pkg/asset/machines/libvirt" + "github.com/openshift/installer/pkg/asset/machines/machineconfig" "github.com/openshift/installer/pkg/asset/machines/openstack" "github.com/openshift/installer/pkg/asset/rhcos" awstypes "github.com/openshift/installer/pkg/types/aws" @@ -22,6 +23,7 @@ import ( nonetypes "github.com/openshift/installer/pkg/types/none" openstacktypes "github.com/openshift/installer/pkg/types/openstack" vspheretypes "github.com/openshift/installer/pkg/types/vsphere" + mcfgv1 "github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1" "github.com/pkg/errors" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/serializer" @@ -33,17 +35,23 @@ import ( // Master generates the machines for the `master` machine pool. type Master struct { - FileList []*asset.File + UserDataFile *asset.File + MachineConfigFiles []*asset.File + MachineFiles []*asset.File } -var ( +const ( directory = "openshift" - // MasterMachineFileName is the format string for constucting the master Machine filenames. - MasterMachineFileName = "99_openshift-cluster-api_master-machines-%s.yaml" + // masterMachineFileName is the format string for constucting the master Machine filenames. + masterMachineFileName = "99_openshift-cluster-api_master-machines-%s.yaml" + + // masterUserDataFileName is the filename used for the master user-data secret. + masterUserDataFileName = "99_openshift-cluster-api_master-user-data-secret.yaml" +) - // MasterUserDataFileName is the filename used for the master user-data secret. - MasterUserDataFileName = "99_openshift-cluster-api_master-user-data-secret.yaml" +var ( + masterMachineFileNamePattern = fmt.Sprintf(masterMachineFileName, "*") _ asset.WritableAsset = (*Master)(nil) ) @@ -82,10 +90,10 @@ func (m *Master) Generate(dependencies asset.Parents) error { mign := &machine.Master{} dependencies.Get(clusterID, installconfig, rhcosImage, mign) - var err error - machines := []machineapi.Machine{} ic := installconfig.Config pool := ic.ControlPlane + var err error + machines := []machineapi.Machine{} switch ic.Platform.Name() { case awstypes.Name: mpool := defaultAWSMachinePoolPlatform() @@ -138,17 +146,19 @@ func (m *Master) Generate(dependencies asset.Parents) error { return errors.Wrap(err, "failed to create user-data secret for master machines") } - m.FileList = []*asset.File{{ - Filename: filepath.Join(directory, MasterUserDataFileName), + m.UserDataFile = &asset.File{ + Filename: filepath.Join(directory, masterUserDataFileName), Data: data, - }} + } - count := len(machines) - if count == 0 { - return errors.New("at least one master machine must be configured") + machineConfigs := []*mcfgv1.MachineConfig{} + m.MachineConfigFiles, err = machineconfig.Manifests(machineConfigs, "master", directory) + if err != nil { + return errors.Wrap(err, "failed to create MachineConfig manifests for master machines") } - padFormat := fmt.Sprintf("%%0%dd", len(fmt.Sprintf("%d", count))) + m.MachineFiles = make([]*asset.File, len(machines)) + padFormat := fmt.Sprintf("%%0%dd", len(fmt.Sprintf("%d", len(machines)))) for i, machine := range machines { data, err := yaml.Marshal(machine) if err != nil { @@ -156,10 +166,10 @@ func (m *Master) Generate(dependencies asset.Parents) error { } padded := fmt.Sprintf(padFormat, i) - m.FileList = append(m.FileList, &asset.File{ - Filename: filepath.Join(directory, fmt.Sprintf(MasterMachineFileName, padded)), + m.MachineFiles[i] = &asset.File{ + Filename: filepath.Join(directory, fmt.Sprintf(masterMachineFileName, padded)), Data: data, - }) + } } return nil @@ -167,42 +177,49 @@ func (m *Master) Generate(dependencies asset.Parents) error { // Files returns the files generated by the asset. func (m *Master) Files() []*asset.File { - return m.FileList + files := make([]*asset.File, 0, 1+len(m.MachineConfigFiles)+len(m.MachineFiles)) + if m.UserDataFile != nil { + files = append(files, m.UserDataFile) + } + files = append(files, m.MachineConfigFiles...) + files = append(files, m.MachineFiles...) + return files } // Load reads the asset files from disk. func (m *Master) Load(f asset.FileFetcher) (found bool, err error) { - file, err := f.FetchByName(filepath.Join(directory, MasterUserDataFileName)) + file, err := f.FetchByName(filepath.Join(directory, masterUserDataFileName)) if err != nil { if os.IsNotExist(err) { return false, nil } return false, err } - m.FileList = []*asset.File{file} + m.UserDataFile = file - fileList, err := f.FetchByPattern(filepath.Join(directory, fmt.Sprintf(MasterMachineFileName, "*"))) + m.MachineConfigFiles, err = machineconfig.Load(f, "master", directory) + if err != nil { + return true, err + } + + fileList, err := f.FetchByPattern(filepath.Join(directory, masterMachineFileNamePattern)) if err != nil { return true, err } if len(fileList) == 0 { - return true, errors.Errorf("master machine manifests are required if you also provide %s", file.Filename) + return true, errors.Errorf("master machine manifests are required if you also provide %s", m.UserDataFile.Filename) } - m.FileList = append(m.FileList, fileList...) + m.MachineFiles = fileList return true, nil } // Machines returns master Machine manifest YAML. func (m *Master) Machines() [][]byte { - machines := [][]byte{} - userData := filepath.Join(directory, MasterUserDataFileName) - for _, file := range m.FileList { - if file.Filename == userData { - continue - } - machines = append(machines, file.Data) + machines := make([][]byte, len(m.MachineFiles)) + for i, file := range m.MachineFiles { + machines[i] = file.Data } return machines } @@ -220,9 +237,9 @@ func (m *Master) StructuredMachines() ([]machineapi.Machine, error) { ) machines := []machineapi.Machine{} - for i, data := range m.Machines() { + for i, file := range m.MachineFiles { machine := &machineapi.Machine{} - err := yaml.Unmarshal(data, &machine) + err := yaml.Unmarshal(file.Data, &machine) if err != nil { return machines, errors.Wrapf(err, "unmarshal master %d", i) } @@ -238,3 +255,23 @@ func (m *Master) StructuredMachines() ([]machineapi.Machine, error) { return machines, nil } + +// IsMasterManifest tests whether a file is a manifest that belongs to the +// Master Machines asset. +func IsMasterManifest(file *asset.File) bool { + if filepath.Dir(file.Filename) != directory { + return false + } + filename := filepath.Base(file.Filename) + if filename == masterUserDataFileName { + return true + } + if machineconfig.IsManifest("master", filename) { + return true + } + if matched, err := filepath.Match(masterMachineFileNamePattern, filename); err != nil { + panic("bad format for master machine file name pattern") + } else { + return matched + } +} diff --git a/pkg/asset/manifests/openshift.go b/pkg/asset/manifests/openshift.go index 5e3db74e6e4..ee033052aee 100644 --- a/pkg/asset/manifests/openshift.go +++ b/pkg/asset/manifests/openshift.go @@ -2,7 +2,6 @@ package manifests import ( "encoding/base64" - "fmt" "path/filepath" "github.com/aws/aws-sdk-go/aws/session" @@ -154,18 +153,8 @@ func (o *Openshift) Load(f asset.FileFetcher) (bool, error) { return false, err } - masterMachinePattern := fmt.Sprintf(machines.MasterMachineFileName, "*") for _, file := range fileList { - filename := filepath.Base(file.Filename) - if filename == machines.MasterUserDataFileName { - continue - } - - matched, err := filepath.Match(masterMachinePattern, filename) - if err != nil { - return true, err - } - if matched { + if machines.IsMasterManifest(file) { continue } From e4d5ec92001c7efe8020b8d3a43188591d7bf9b5 Mon Sep 17 00:00:00 2001 From: Abhinav Dahiya Date: Mon, 25 Mar 2019 15:04:04 -0700 Subject: [PATCH 4/5] asset/machines/worker: allow adding MachineConfigs for compute machinepools This sets up the worker machine asset to allow adding MachineConfigs. A list of machine configs can be added to compute machinepools that will be created alongside the user-data and machineset objects. --- pkg/asset/machines/worker.go | 58 +++++++++++++++++++++++++------- pkg/asset/manifests/openshift.go | 7 ++-- 2 files changed, 48 insertions(+), 17 deletions(-) diff --git a/pkg/asset/machines/worker.go b/pkg/asset/machines/worker.go index 1244ec87300..b75e45c3d7f 100644 --- a/pkg/asset/machines/worker.go +++ b/pkg/asset/machines/worker.go @@ -1,9 +1,8 @@ package machines import ( - "bytes" "fmt" - "text/template" + "path/filepath" "github.com/ghodss/yaml" "github.com/pkg/errors" @@ -15,6 +14,7 @@ import ( "github.com/openshift/installer/pkg/asset/installconfig" "github.com/openshift/installer/pkg/asset/machines/aws" "github.com/openshift/installer/pkg/asset/machines/libvirt" + "github.com/openshift/installer/pkg/asset/machines/machineconfig" "github.com/openshift/installer/pkg/asset/machines/openstack" "github.com/openshift/installer/pkg/asset/rhcos" awstypes "github.com/openshift/installer/pkg/types/aws" @@ -23,6 +23,15 @@ import ( nonetypes "github.com/openshift/installer/pkg/types/none" openstacktypes "github.com/openshift/installer/pkg/types/openstack" vspheretypes "github.com/openshift/installer/pkg/types/vsphere" + mcfgv1 "github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1" +) + +const ( + // workerMachineSetFileName is the filename used for the worker MachineSet manifest. + workerMachineSetFileName = "99_openshift-cluster-api_worker-machineset.yaml" + + // workerUserDataFileName is the filename used for the worker user-data secret. + workerUserDataFileName = "99_openshift-cluster-api_worker-user-data-secret.yaml" ) func defaultAWSMachinePoolPlatform() awstypes.MachinePool { @@ -46,8 +55,9 @@ func defaultOpenStackMachinePoolPlatform(flavor string) openstacktypes.MachinePo // Worker generates the machinesets for `worker` machine pool. type Worker struct { - MachineSetRaw []byte - UserDataSecretRaw []byte + UserDataFile *asset.File + MachineConfigFiles []*asset.File + MachineSetFile *asset.File } var _ asset.Asset = (*Worker)(nil) @@ -86,13 +96,17 @@ func (w *Worker) Generate(dependencies asset.Parents) error { wign := &machine.Worker{} dependencies.Get(clusterID, installconfig, rhcosImage, wign) - var err error userDataMap := map[string][]byte{"worker-user-data": wign.File.Data} - w.UserDataSecretRaw, err = userDataList(userDataMap) + data, err := userDataList(userDataMap) if err != nil { return errors.Wrap(err, "failed to create user-data secret for worker machines") } + w.UserDataFile = &asset.File{ + Filename: filepath.Join(directory, workerUserDataFileName), + Data: data, + } + machineConfigs := []*mcfgv1.MachineConfig{} machineSets := []runtime.Object{} ic := installconfig.Config for _, pool := range ic.Compute { @@ -150,6 +164,11 @@ func (w *Worker) Generate(dependencies asset.Parents) error { } } + w.MachineConfigFiles, err = machineconfig.Manifests(machineConfigs, "worker", directory) + if err != nil { + return errors.Wrap(err, "failed to create MachineConfig manifests for worker machines") + } + if len(machineSets) == 0 { return nil } @@ -163,18 +182,31 @@ func (w *Worker) Generate(dependencies asset.Parents) error { for i, set := range machineSets { list.Items[i] = runtime.RawExtension{Object: set} } - raw, err := yaml.Marshal(list) + data, err = yaml.Marshal(list) if err != nil { return errors.Wrap(err, "failed to marshal") } - w.MachineSetRaw = raw + w.MachineSetFile = &asset.File{ + Filename: filepath.Join(directory, workerMachineSetFileName), + Data: data, + } return nil } -func applyTemplateData(template *template.Template, templateData interface{}) []byte { - buf := &bytes.Buffer{} - if err := template.Execute(buf, templateData); err != nil { - panic(err) +// Files returns the files generated by the asset. +func (w *Worker) Files() []*asset.File { + files := make([]*asset.File, 0, 1+len(w.MachineConfigFiles)+1) + if w.UserDataFile != nil { + files = append(files, w.UserDataFile) + } + files = append(files, w.MachineConfigFiles...) + if w.MachineSetFile != nil { + files = append(files, w.MachineSetFile) } - return buf.Bytes() + return files +} + +// Load returns false since this asset is not written to disk by the installer. +func (w *Worker) Load(f asset.FileFetcher) (found bool, err error) { + return false, nil } diff --git a/pkg/asset/manifests/openshift.go b/pkg/asset/manifests/openshift.go index ee033052aee..9ed55e85441 100644 --- a/pkg/asset/manifests/openshift.go +++ b/pkg/asset/manifests/openshift.go @@ -113,10 +113,8 @@ func (o *Openshift) Generate(dependencies asset.Parents) error { roleCloudCredsSecretReader) assetData := map[string][]byte{ - "99_binding-discovery.yaml": []byte(bindingDiscovery.Files()[0].Data), - "99_kubeadmin-password-secret.yaml": applyTemplateData(kubeadminPasswordSecret.Files()[0].Data, templateData), - "99_openshift-cluster-api_worker-machineset.yaml": worker.MachineSetRaw, - "99_openshift-cluster-api_worker-user-data-secret.yaml": worker.UserDataSecretRaw, + "99_binding-discovery.yaml": []byte(bindingDiscovery.Files()[0].Data), + "99_kubeadmin-password-secret.yaml": applyTemplateData(kubeadminPasswordSecret.Files()[0].Data, templateData), } switch platform { @@ -135,6 +133,7 @@ func (o *Openshift) Generate(dependencies asset.Parents) error { Data: data, }) } + o.FileList = append(o.FileList, worker.Files()...) asset.SortFiles(o.FileList) From 5623cbdb6957c60240e57dc45bd0bbf243e31682 Mon Sep 17 00:00:00 2001 From: Abhinav Dahiya Date: Mon, 25 Mar 2019 15:55:46 -0700 Subject: [PATCH 5/5] machines: add the authorized keys for a pool using a machine config `cluster-config-v1` is being deprecated in favor of global configs [1] and Machine Config Operator needs to drop using the `SSHKey` in install-config [2] to setup the `SSHAuthorizedKeys` for `core` user. This pushes a machineconfig with the `SSHAuthorizedKeys` sourced from [2] for each machinepool, so that Machine Config Operator can drop generating the machineconfig using the `cluster-config-v1` config map in the cluster. [1]: https://github.com/openshift/installer/issues/680 [2]: https://godoc.org/github.com/openshift/installer/pkg/types#InstallConfig --- .../machines/machineconfig/authorizedkeys.go | 37 ++++++ pkg/asset/machines/master.go | 3 + pkg/asset/machines/master_test.go | 111 +++++++++++++++++ pkg/asset/machines/worker.go | 3 + pkg/asset/machines/worker_test.go | 113 ++++++++++++++++++ 5 files changed, 267 insertions(+) create mode 100644 pkg/asset/machines/machineconfig/authorizedkeys.go create mode 100644 pkg/asset/machines/master_test.go create mode 100644 pkg/asset/machines/worker_test.go diff --git a/pkg/asset/machines/machineconfig/authorizedkeys.go b/pkg/asset/machines/machineconfig/authorizedkeys.go new file mode 100644 index 00000000000..973641839cb --- /dev/null +++ b/pkg/asset/machines/machineconfig/authorizedkeys.go @@ -0,0 +1,37 @@ +package machineconfig + +import ( + "fmt" + + ignv2_2types "github.com/coreos/ignition/config/v2_2/types" + mcfgv1 "github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// ForAuthorizedKeys creates the MachineConfig to set the authorized key for `core` user. +func ForAuthorizedKeys(key string, role string) *mcfgv1.MachineConfig { + return &mcfgv1.MachineConfig{ + TypeMeta: metav1.TypeMeta{ + APIVersion: mcfgv1.SchemeGroupVersion.String(), + Kind: "MachineConfig", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("99-%s-ssh", role), + Labels: map[string]string{ + "machineconfiguration.openshift.io/role": role, + }, + }, + Spec: mcfgv1.MachineConfigSpec{ + Config: ignv2_2types.Config{ + Ignition: ignv2_2types.Ignition{ + Version: ignv2_2types.MaxVersion.String(), + }, + Passwd: ignv2_2types.Passwd{ + Users: []ignv2_2types.PasswdUser{{ + Name: "core", SSHAuthorizedKeys: []ignv2_2types.SSHAuthorizedKey{ignv2_2types.SSHAuthorizedKey(key)}, + }}, + }, + }, + }, + } +} diff --git a/pkg/asset/machines/master.go b/pkg/asset/machines/master.go index 798cb2e9edd..9b2f9e38608 100644 --- a/pkg/asset/machines/master.go +++ b/pkg/asset/machines/master.go @@ -152,6 +152,9 @@ func (m *Master) Generate(dependencies asset.Parents) error { } machineConfigs := []*mcfgv1.MachineConfig{} + if ic.SSHKey != "" { + machineConfigs = append(machineConfigs, machineconfig.ForAuthorizedKeys(ic.SSHKey, "master")) + } m.MachineConfigFiles, err = machineconfig.Manifests(machineConfigs, "master", directory) if err != nil { return errors.Wrap(err, "failed to create MachineConfig manifests for master machines") diff --git a/pkg/asset/machines/master_test.go b/pkg/asset/machines/master_test.go new file mode 100644 index 00000000000..99232f98fb2 --- /dev/null +++ b/pkg/asset/machines/master_test.go @@ -0,0 +1,111 @@ +package machines + +import ( + "testing" + + "github.com/stretchr/testify/assert" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/pointer" + + "github.com/openshift/installer/pkg/asset" + "github.com/openshift/installer/pkg/asset/ignition/machine" + "github.com/openshift/installer/pkg/asset/installconfig" + "github.com/openshift/installer/pkg/asset/rhcos" + "github.com/openshift/installer/pkg/types" + awstypes "github.com/openshift/installer/pkg/types/aws" +) + +func TestMasterGenerateMachineConfigs(t *testing.T) { + cases := []struct { + name string + key string + expectedMachineConfig string + }{ + { + name: "no key", + }, + { + name: "key present", + key: "ssh-rsa: dummy-key", + expectedMachineConfig: `--- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +metadata: + creationTimestamp: null + labels: + machineconfiguration.openshift.io/role: master + name: 99-master-ssh +spec: + config: + ignition: + config: {} + security: + tls: {} + timeouts: {} + version: 2.2.0 + networkd: {} + passwd: + users: + - name: core + sshAuthorizedKeys: + - 'ssh-rsa: dummy-key' + storage: {} + systemd: {} + osImageURL: "" +`, + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + parents := asset.Parents{} + parents.Add( + &installconfig.ClusterID{ + UUID: "test-uuid", + InfraID: "test-infra-id", + }, + &installconfig.InstallConfig{ + Config: &types.InstallConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-cluster", + }, + SSHKey: tc.key, + BaseDomain: "test-domain", + Platform: types.Platform{ + AWS: &awstypes.Platform{ + Region: "us-east-1", + }, + }, + ControlPlane: &types.MachinePool{ + Replicas: pointer.Int64Ptr(1), + Platform: types.MachinePoolPlatform{ + AWS: &awstypes.MachinePool{ + Zones: []string{"us-east-1a"}, + }, + }, + }, + }, + }, + (*rhcos.Image)(pointer.StringPtr("test-image")), + &machine.Master{ + File: &asset.File{ + Filename: "master-ignition", + Data: []byte("test-ignition"), + }, + }, + ) + master := &Master{} + if err := master.Generate(parents); err != nil { + t.Fatalf("failed to generate master machines: %v", err) + } + if tc.expectedMachineConfig != "" { + if assert.Equal(t, 1, len(master.MachineConfigFiles), "expected one machine config file") { + file := master.MachineConfigFiles[0] + assert.Equal(t, "openshift/99_openshift-machineconfig_master.yaml", file.Filename, "unexpected machine config filename") + assert.Equal(t, tc.expectedMachineConfig, string(file.Data), "unexepcted machine config contents") + } + } else { + assert.Equal(t, 0, len(master.MachineConfigFiles), "expected no machine config files") + } + }) + } +} diff --git a/pkg/asset/machines/worker.go b/pkg/asset/machines/worker.go index b75e45c3d7f..885e898e9ae 100644 --- a/pkg/asset/machines/worker.go +++ b/pkg/asset/machines/worker.go @@ -162,6 +162,9 @@ func (w *Worker) Generate(dependencies asset.Parents) error { default: return fmt.Errorf("invalid Platform") } + if ic.SSHKey != "" { + machineConfigs = append(machineConfigs, machineconfig.ForAuthorizedKeys(ic.SSHKey, "worker")) + } } w.MachineConfigFiles, err = machineconfig.Manifests(machineConfigs, "worker", directory) diff --git a/pkg/asset/machines/worker_test.go b/pkg/asset/machines/worker_test.go new file mode 100644 index 00000000000..17e03f30e09 --- /dev/null +++ b/pkg/asset/machines/worker_test.go @@ -0,0 +1,113 @@ +package machines + +import ( + "testing" + + "github.com/stretchr/testify/assert" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/pointer" + + "github.com/openshift/installer/pkg/asset" + "github.com/openshift/installer/pkg/asset/ignition/machine" + "github.com/openshift/installer/pkg/asset/installconfig" + "github.com/openshift/installer/pkg/asset/rhcos" + "github.com/openshift/installer/pkg/types" + awstypes "github.com/openshift/installer/pkg/types/aws" +) + +func TestWorkerGenerate(t *testing.T) { + cases := []struct { + name string + key string + expectedMachineConfig string + }{ + { + name: "no key", + }, + { + name: "key present", + key: "ssh-rsa: dummy-key", + expectedMachineConfig: `--- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +metadata: + creationTimestamp: null + labels: + machineconfiguration.openshift.io/role: worker + name: 99-worker-ssh +spec: + config: + ignition: + config: {} + security: + tls: {} + timeouts: {} + version: 2.2.0 + networkd: {} + passwd: + users: + - name: core + sshAuthorizedKeys: + - 'ssh-rsa: dummy-key' + storage: {} + systemd: {} + osImageURL: "" +`, + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + parents := asset.Parents{} + parents.Add( + &installconfig.ClusterID{ + UUID: "test-uuid", + InfraID: "test-infra-id", + }, + &installconfig.InstallConfig{ + Config: &types.InstallConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-cluster", + }, + SSHKey: tc.key, + BaseDomain: "test-domain", + Platform: types.Platform{ + AWS: &awstypes.Platform{ + Region: "us-east-1", + }, + }, + Compute: []types.MachinePool{ + { + Replicas: pointer.Int64Ptr(1), + Platform: types.MachinePoolPlatform{ + AWS: &awstypes.MachinePool{ + Zones: []string{"us-east-1a"}, + }, + }, + }, + }, + }, + }, + (*rhcos.Image)(pointer.StringPtr("test-image")), + &machine.Worker{ + File: &asset.File{ + Filename: "worker-ignition", + Data: []byte("test-ignition"), + }, + }, + ) + worker := &Worker{} + if err := worker.Generate(parents); err != nil { + t.Fatalf("failed to generate worker machines: %v", err) + } + if tc.expectedMachineConfig != "" { + if assert.Equal(t, 1, len(worker.MachineConfigFiles), "expected one machine config file") { + file := worker.MachineConfigFiles[0] + assert.Equal(t, "openshift/99_openshift-machineconfig_worker.yaml", file.Filename, "unexpected machine config filename") + assert.Equal(t, tc.expectedMachineConfig, string(file.Data), "unexepcted machine config contents") + } + } else { + assert.Equal(t, 0, len(worker.MachineConfigFiles), "expected no machine config files") + } + }) + } +}