mirror of https://github.com/containers/podman.git
222 lines
5.4 KiB
Go
222 lines
5.4 KiB
Go
// +build amd64,linux arm64,linux amd64,darwin arm64,darwin
|
|
|
|
package machine
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
)
|
|
|
|
/*
|
|
If this file gets too nuts, we can perhaps use existing go code
|
|
to create ignition files. At this point, the file is so simple
|
|
that I chose to use structs and not import any code as I was
|
|
concerned (unsubstantiated) about too much bloat coming in.
|
|
|
|
https://github.com/openshift/machine-config-operator/blob/master/pkg/server/server.go
|
|
*/
|
|
|
|
// Convenience function to convert int to ptr
|
|
func intToPtr(i int) *int {
|
|
return &i
|
|
}
|
|
|
|
// Convenience function to convert string to ptr
|
|
func strToPtr(s string) *string {
|
|
return &s
|
|
}
|
|
|
|
// Convenience function to convert bool to ptr
|
|
func boolToPtr(b bool) *bool {
|
|
return &b
|
|
}
|
|
|
|
func getNodeUsr(usrName string) NodeUser {
|
|
return NodeUser{Name: &usrName}
|
|
}
|
|
|
|
func getNodeGrp(grpName string) NodeGroup {
|
|
return NodeGroup{Name: &grpName}
|
|
}
|
|
|
|
type DynamicIgnition struct {
|
|
Name string
|
|
Key string
|
|
VMName string
|
|
WritePath string
|
|
}
|
|
|
|
// NewIgnitionFile
|
|
func NewIgnitionFile(ign DynamicIgnition) error {
|
|
if len(ign.Name) < 1 {
|
|
ign.Name = DefaultIgnitionUserName
|
|
}
|
|
ignVersion := Ignition{
|
|
Version: "3.2.0",
|
|
}
|
|
|
|
ignPassword := Passwd{
|
|
Users: []PasswdUser{
|
|
{
|
|
Name: ign.Name,
|
|
SSHAuthorizedKeys: []SSHAuthorizedKey{SSHAuthorizedKey(ign.Key)},
|
|
},
|
|
{
|
|
Name: "root",
|
|
SSHAuthorizedKeys: []SSHAuthorizedKey{SSHAuthorizedKey(ign.Key)},
|
|
},
|
|
},
|
|
}
|
|
|
|
ignStorage := Storage{
|
|
Directories: getDirs(ign.Name),
|
|
Files: getFiles(ign.Name),
|
|
Links: getLinks(ign.Name),
|
|
}
|
|
|
|
// ready is a unit file that sets up the virtual serial device
|
|
// where when the VM is done configuring, it will send an ack
|
|
// so a listening host knows it can being interacting with it
|
|
ready := `[Unit]
|
|
Requires=dev-virtio\\x2dports-%s.device
|
|
OnFailure=emergency.target
|
|
OnFailureJobMode=isolate
|
|
[Service]
|
|
Type=oneshot
|
|
RemainAfterExit=yes
|
|
ExecStart=/bin/sh -c '/usr/bin/echo Ready >/dev/%s'
|
|
[Install]
|
|
RequiredBy=multi-user.target
|
|
`
|
|
_ = ready
|
|
ignSystemd := Systemd{
|
|
Units: []Unit{
|
|
{
|
|
Enabled: boolToPtr(true),
|
|
Name: "podman.socket",
|
|
},
|
|
{
|
|
Enabled: boolToPtr(true),
|
|
Name: "ready.service",
|
|
Contents: strToPtr(fmt.Sprintf(ready, "vport1p1", "vport1p1")),
|
|
},
|
|
}}
|
|
ignConfig := Config{
|
|
Ignition: ignVersion,
|
|
Passwd: ignPassword,
|
|
Storage: ignStorage,
|
|
Systemd: ignSystemd,
|
|
}
|
|
b, err := json.Marshal(ignConfig)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return ioutil.WriteFile(ign.WritePath, b, 0644)
|
|
}
|
|
|
|
func getDirs(usrName string) []Directory {
|
|
// Ignition has a bug/feature? where if you make a series of dirs
|
|
// in one swoop, then the leading dirs are creates as root.
|
|
newDirs := []string{
|
|
"/home/" + usrName + "/.config",
|
|
"/home/" + usrName + "/.config/containers",
|
|
"/home/" + usrName + "/.config/systemd",
|
|
"/home/" + usrName + "/.config/systemd/user",
|
|
"/home/" + usrName + "/.config/systemd/user/default.target.wants",
|
|
}
|
|
var (
|
|
dirs = make([]Directory, len(newDirs))
|
|
)
|
|
for i, d := range newDirs {
|
|
newDir := Directory{
|
|
Node: Node{
|
|
Group: getNodeGrp(usrName),
|
|
Path: d,
|
|
User: getNodeUsr(usrName),
|
|
},
|
|
DirectoryEmbedded1: DirectoryEmbedded1{Mode: intToPtr(493)},
|
|
}
|
|
dirs[i] = newDir
|
|
}
|
|
return dirs
|
|
}
|
|
|
|
func getFiles(usrName string) []File {
|
|
var (
|
|
files []File
|
|
)
|
|
// Add a fake systemd service to get the user socket rolling
|
|
files = append(files, File{
|
|
Node: Node{
|
|
Group: getNodeGrp(usrName),
|
|
Path: "/home/" + usrName + "/.config/systemd/user/linger-example.service",
|
|
User: getNodeUsr(usrName),
|
|
},
|
|
FileEmbedded1: FileEmbedded1{
|
|
Append: nil,
|
|
Contents: Resource{
|
|
Source: strToPtr("data:,%5BUnit%5D%0ADescription%3DA%20systemd%20user%20unit%20demo%0AAfter%3Dnetwork-online.target%0AWants%3Dnetwork-online.target%20podman.socket%0A%5BService%5D%0AExecStart%3D%2Fusr%2Fbin%2Fsleep%20infinity%0A"),
|
|
},
|
|
Mode: intToPtr(484),
|
|
},
|
|
})
|
|
|
|
// Set containers.conf up for core user to use cni networks
|
|
// by default
|
|
files = append(files, File{
|
|
Node: Node{
|
|
Group: getNodeGrp(usrName),
|
|
Path: "/home/" + usrName + "/.config/containers/containers.conf",
|
|
User: getNodeUsr(usrName),
|
|
},
|
|
FileEmbedded1: FileEmbedded1{
|
|
Append: nil,
|
|
Contents: Resource{
|
|
Source: strToPtr("data:,%5Bcontainers%5D%0D%0Anetns%3D%22bridge%22%0D%0Arootless_networking%3D%22cni%22"),
|
|
},
|
|
Mode: intToPtr(484),
|
|
},
|
|
})
|
|
// Add a file into linger
|
|
files = append(files, File{
|
|
Node: Node{
|
|
Group: getNodeGrp(usrName),
|
|
Path: "/var/lib/systemd/linger/core",
|
|
User: getNodeUsr(usrName),
|
|
},
|
|
FileEmbedded1: FileEmbedded1{Mode: intToPtr(420)},
|
|
})
|
|
|
|
// Set machine_enabled to true to indicate we're in a VM
|
|
files = append(files, File{
|
|
Node: Node{
|
|
Group: getNodeGrp("root"),
|
|
Path: "/etc/containers/containers.conf",
|
|
User: getNodeUsr("root"),
|
|
},
|
|
FileEmbedded1: FileEmbedded1{
|
|
Append: nil,
|
|
Contents: Resource{
|
|
Source: strToPtr("data:,%5Bengine%5D%0Amachine_enabled%3Dtrue%0A"),
|
|
},
|
|
Mode: intToPtr(420),
|
|
},
|
|
})
|
|
return files
|
|
}
|
|
|
|
func getLinks(usrName string) []Link {
|
|
return []Link{{
|
|
Node: Node{
|
|
Group: getNodeGrp(usrName),
|
|
Path: "/home/" + usrName + "/.config/systemd/user/default.target.wants/linger-example.service",
|
|
User: getNodeUsr(usrName),
|
|
},
|
|
LinkEmbedded1: LinkEmbedded1{
|
|
Hard: boolToPtr(false),
|
|
Target: "/home/" + usrName + "/.config/systemd/user/linger-example.service",
|
|
},
|
|
}}
|
|
}
|