diff --git a/coreos-cloudinit.go b/coreos-cloudinit.go index d52757f..97dbad3 100644 --- a/coreos-cloudinit.go +++ b/coreos-cloudinit.go @@ -31,6 +31,7 @@ import ( "github.com/flatcar/coreos-cloudinit/datasource" "github.com/flatcar/coreos-cloudinit/datasource/configdrive" "github.com/flatcar/coreos-cloudinit/datasource/file" + "github.com/flatcar/coreos-cloudinit/datasource/ionoscloud" "github.com/flatcar/coreos-cloudinit/datasource/metadata/cloudsigma" "github.com/flatcar/coreos-cloudinit/datasource/metadata/digitalocean" "github.com/flatcar/coreos-cloudinit/datasource/metadata/ec2" @@ -66,6 +67,7 @@ var ( cloudSigmaMetadataService bool digitalOceanMetadataService string packetMetadataService string + ionosCloudSeed string url string procCmdLine bool vmware bool @@ -96,6 +98,7 @@ func init() { flag.BoolVar(&flags.sources.procCmdLine, "from-proc-cmdline", false, fmt.Sprintf("Parse %s for '%s=', using the cloud-config served by an HTTP GET to ", proc_cmdline.ProcCmdlineLocation, proc_cmdline.ProcCmdlineCloudConfigFlag)) flag.BoolVar(&flags.sources.vmware, "from-vmware-guestinfo", false, "Read data from VMware guestinfo") flag.StringVar(&flags.sources.ovfEnv, "from-vmware-ovf-env", "", "Read data from OVF Environment") + flag.StringVar(&flags.sources.ionosCloudSeed, "from-ionoscloud-seed", "", "Read data from IONOS Cloud injected cloud-init seed files") flag.StringVar(&flags.oem, "oem", "", "Use the settings specific to the provided OEM") flag.StringVar(&flags.convertNetconf, "convert-netconf", "", "Read the network config provided in cloud-drive and translate it from the specified format into networkd unit files") flag.StringVar(&flags.workspace, "workspace", "/var/lib/coreos-cloudinit", "Base directory coreos-cloudinit should use to store data") @@ -134,6 +137,10 @@ var ( "from-vmware-guestinfo": "true", "convert-netconf": "vmware", }, + "ionoscloud": { + "from-ionoscloud-seed": "/var/lib/cloud/seed/nocloud/", + "convert-netconf": "debian", + }, } ) @@ -178,7 +185,7 @@ func main() { dss := getDatasources() if len(dss) == 0 { - fmt.Println("Provide at least one of --from-file, --from-configdrive, --from-ec2-metadata, --from-gce-metadata, --from-cloudsigma-metadata, --from-packet-metadata, --from-digitalocean-metadata, --from-vmware-guestinfo, --from-waagent, --from-url or --from-proc-cmdline") + fmt.Println("Provide at least one of --from-file, --from-configdrive, --from-ec2-metadata, --from-gce-metadata, --from-cloudsigma-metadata, --from-packet-metadata, --from-digitalocean-metadata, --from-ionoscloud-seed, --from-vmware-guestinfo, --from-waagent, --from-url or --from-proc-cmdline") os.Exit(2) } @@ -369,6 +376,9 @@ func getDatasources() []datasource.Datasource { if flags.sources.ovfEnv != "" { dss = append(dss, vmware.NewDatasource(flags.sources.ovfEnv)) } + if flags.sources.ionosCloudSeed != "" { + dss = append(dss, ionoscloud.NewDatasource(flags.sources.ionosCloudSeed)) + } return dss } diff --git a/datasource/ionoscloud/ionoscloud.go b/datasource/ionoscloud/ionoscloud.go new file mode 100644 index 0000000..fd0c424 --- /dev/null +++ b/datasource/ionoscloud/ionoscloud.go @@ -0,0 +1,74 @@ +package ionoscloud + +import ( + "log" + "os" + "path" + + "gopkg.in/yaml.v3" + + "github.com/flatcar/coreos-cloudinit/datasource" +) + +const ( + networkconfig = "/etc/cloud/cloud.cfg.d/99_custom_networking.cfg" +) + +type ionoscloud struct { + seed string + readFile func(filename string) ([]byte, error) +} + +func NewDatasource(seed string) *ionoscloud { + return &ionoscloud{seed, os.ReadFile} +} + +func (ic *ionoscloud) IsAvailable() bool { + _, err := os.Stat(ic.seed) + return !os.IsNotExist(err) +} + +func (ic *ionoscloud) AvailabilityChanges() bool { + return true +} + +func (ic *ionoscloud) ConfigRoot() string { + return ic.seed +} + +func (ic *ionoscloud) FetchMetadata() (metadata datasource.Metadata, err error) { + var data []byte + var m struct { + DSMode string `json:"dsmode"` + SSHPublicKeys map[string]string `json:"public_keys"` + } + + if data, err = ic.tryReadFile(path.Join(ic.seed, "meta-data")); err != nil || len(data) == 0 { + return + } + if err = yaml.Unmarshal([]byte(data), &m); err != nil { + return + } + + metadata.SSHPublicKeys = m.SSHPublicKeys + metadata.NetworkConfig, _ = ic.tryReadFile(networkconfig) + + return +} + +func (ic *ionoscloud) FetchUserdata() ([]byte, error) { + return ic.tryReadFile(path.Join(ic.seed, "user-data")) +} + +func (ic *ionoscloud) Type() string { + return "ionoscloud" +} + +func (ic *ionoscloud) tryReadFile(filename string) ([]byte, error) { + log.Printf("Attempting to read from %q\n", filename) + data, err := os.ReadFile(filename) + if os.IsNotExist(err) { + err = nil + } + return data, err +} diff --git a/units/user-configdrive.service b/units/user-configdrive.service index 99737c0..3f02f6e 100644 --- a/units/user-configdrive.service +++ b/units/user-configdrive.service @@ -16,10 +16,12 @@ Before=user-config.target After=enable-oem-cloudinit.service oem-cloudinit.service # Skip on clouds that are covered by flatcar/init:systemd/system/oem-cloudinit.service -ConditionKernelCommandLine=!flatcar.oem.id=digitalocean ConditionKernelCommandLine=!coreos.oem.id=digitalocean +ConditionKernelCommandLine=!flatcar.oem.id=digitalocean ConditionKernelCommandLine=!coreos.oem.id=openstack ConditionKernelCommandLine=!flatcar.oem.id=openstack +ConditionKernelCommandLine=!coreos.oem.id=ionoscloud +ConditionKernelCommandLine=!flatcar.oem.id=ionoscloud [Service] Type=oneshot ExecCondition=/usr/bin/bash -c "if [ -f '/etc/.ignition-result.json' ] && /usr/bin/jq -e '.userConfigProvided == true' /etc/.ignition-result.json; then exit 1; fi"