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/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/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 +} diff --git a/pkg/asset/machines/master.go b/pkg/asset/machines/master.go index a3147b8f73e..9b2f9e38608 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,22 @@ 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{} + 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") } - 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 +169,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 +180,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 + + m.MachineConfigFiles, err = machineconfig.Load(f, "master", directory) + if err != nil { + return true, err + } - fileList, err := f.FetchByPattern(filepath.Join(directory, fmt.Sprintf(MasterMachineFileName, "*"))) + 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 +240,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 +258,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/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 1244ec87300..885e898e9ae 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 { @@ -148,6 +162,14 @@ 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) + if err != nil { + return errors.Wrap(err, "failed to create MachineConfig manifests for worker machines") } if len(machineSets) == 0 { @@ -163,18 +185,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) } - return buf.Bytes() + files = append(files, w.MachineConfigFiles...) + if w.MachineSetFile != nil { + files = append(files, w.MachineSetFile) + } + 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/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") + } + }) + } +} diff --git a/pkg/asset/manifests/openshift.go b/pkg/asset/manifests/openshift.go index 5e3db74e6e4..9ed55e85441 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" @@ -114,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 { @@ -136,6 +133,7 @@ func (o *Openshift) Generate(dependencies asset.Parents) error { Data: data, }) } + o.FileList = append(o.FileList, worker.Files()...) asset.SortFiles(o.FileList) @@ -154,18 +152,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 } 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 +}