mirror of
https://github.com/linuxkit/linuxkit.git
synced 2025-11-14 03:23:31 +00:00
This checks if any CDROM in the system has metadata. Useful if you boot off CDROM and also have a metadata CDROM. Also switch the metadata container to read only, as it is only writing to bind mounted in directories, and /tmp. Signed-off-by: Justin Cormack <justin.cormack@docker.com>
187 lines
4.4 KiB
Go
187 lines
4.4 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"io/ioutil"
|
|
"log"
|
|
"os"
|
|
"path"
|
|
"strconv"
|
|
"syscall"
|
|
)
|
|
|
|
const (
|
|
// ConfigPath is where the data is extracted to
|
|
ConfigPath = "/var/config"
|
|
|
|
// Hostname is the filename in configPath where the hostname is stored
|
|
Hostname = "hostname"
|
|
|
|
// SSH is the path where sshd configuration from the provider is stored
|
|
SSH = "ssh"
|
|
)
|
|
|
|
// Provider is a generic interface for metadata/userdata providers.
|
|
type Provider interface {
|
|
// String should return a unique name for the Provider
|
|
String() string
|
|
|
|
// Probe returns true if the provider was detected.
|
|
Probe() bool
|
|
|
|
// Extract user data. This may write some data, specific to a
|
|
// provider, to ConfigPath and should return the generic userdata.
|
|
Extract() ([]byte, error)
|
|
}
|
|
|
|
// netProviders is a list of Providers offering metadata/userdata over the network
|
|
var netProviders []Provider
|
|
|
|
// cdromProviders is a list of Providers offering metadata/userdata data via CDROM
|
|
var cdromProviders []Provider
|
|
|
|
func init() {
|
|
netProviders = []Provider{NewGCP(), NewVultr(), NewAWS()}
|
|
cdromProviders = ListCDROMs()
|
|
}
|
|
|
|
func main() {
|
|
if err := os.MkdirAll(ConfigPath, 0755); err != nil {
|
|
log.Fatalf("Could not create %s: %s", ConfigPath, err)
|
|
}
|
|
|
|
var p Provider
|
|
var userdata []byte
|
|
var err error
|
|
found := false
|
|
for _, p = range netProviders {
|
|
if p.Probe() {
|
|
log.Printf("%s: Probe succeeded", p)
|
|
userdata, err = p.Extract()
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
for _, p = range cdromProviders {
|
|
log.Printf("Trying %s", p.String())
|
|
if p.Probe() {
|
|
log.Printf("%s: Probe succeeded", p)
|
|
userdata, err = p.Extract()
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
if !found {
|
|
log.Printf("No metadata/userdata found. Bye")
|
|
return
|
|
}
|
|
|
|
if err != nil {
|
|
log.Printf("Error during metadata probe: %s", err)
|
|
}
|
|
|
|
err = ioutil.WriteFile(path.Join(ConfigPath, "provider"), []byte(p.String()), 0644)
|
|
if err != nil {
|
|
log.Printf("Error writing metadata provider: %s", err)
|
|
}
|
|
|
|
if userdata != nil {
|
|
if err := processUserData(userdata); err != nil {
|
|
log.Printf("Could not extract user data: %s", err)
|
|
}
|
|
}
|
|
|
|
// Handle setting the hostname as a special case. We want to
|
|
// do this early and don't really want another container for it.
|
|
hostname, err := ioutil.ReadFile(path.Join(ConfigPath, Hostname))
|
|
if err == nil {
|
|
err := syscall.Sethostname(hostname)
|
|
if err != nil {
|
|
log.Printf("Failed to set hostname: %s", err)
|
|
} else {
|
|
log.Printf("Set hostname to: %s", string(hostname))
|
|
}
|
|
}
|
|
}
|
|
|
|
// If the userdata is a json file, create a directory/file hierarchy.
|
|
// Example:
|
|
// {
|
|
// "foobar" : {
|
|
// "foo" : {
|
|
// "perm": "0644",
|
|
// "content": "hello"
|
|
// }
|
|
// }
|
|
// Will create foobar/foo with mode 0644 and content "hello"
|
|
func processUserData(data []byte) error {
|
|
// Always write the raw data to a file
|
|
err := ioutil.WriteFile(path.Join(ConfigPath, "userdata"), data, 0644)
|
|
if err != nil {
|
|
log.Printf("Could not write userdata: %s", err)
|
|
return err
|
|
}
|
|
|
|
var fd interface{}
|
|
if err := json.Unmarshal(data, &fd); err != nil {
|
|
// Userdata is no JSON, presumably...
|
|
log.Printf("Could not unmarshall userdata: %s", err)
|
|
// This is not an error
|
|
return nil
|
|
}
|
|
cm, ok := fd.(map[string]interface{})
|
|
if !ok {
|
|
log.Printf("Could convert JSON to desired format: %s", fd)
|
|
return nil
|
|
}
|
|
for d, val := range cm {
|
|
dir := path.Join(ConfigPath, d)
|
|
if err := os.MkdirAll(dir, 0755); err != nil {
|
|
log.Printf("Failed to create %s: %s", dir, err)
|
|
continue
|
|
}
|
|
files, ok := val.(map[string]interface{})
|
|
if !ok {
|
|
log.Printf("Could convert JSON for files: %s", val)
|
|
continue
|
|
}
|
|
for f, i := range files {
|
|
p := uint64(0644)
|
|
var c string
|
|
|
|
switch fi := i.(type) {
|
|
case map[string]interface{}:
|
|
if _, ok := fi["perm"]; !ok {
|
|
log.Printf("No permission provided %s", f)
|
|
continue
|
|
}
|
|
if _, ok := fi["content"]; !ok {
|
|
log.Printf("No content provided %s", f)
|
|
continue
|
|
}
|
|
c = fi["content"].(string)
|
|
if p, err = strconv.ParseUint(fi["perm"].(string), 8, 32); err != nil {
|
|
log.Printf("Failed to parse permission %s: %s", fi, err)
|
|
continue
|
|
}
|
|
case string:
|
|
c = fi
|
|
default:
|
|
log.Printf("Couldn't convert JSON for items: %s", i)
|
|
continue
|
|
}
|
|
|
|
if err := ioutil.WriteFile(path.Join(dir, f), []byte(c), os.FileMode(p)); err != nil {
|
|
log.Printf("Failed to write %s/%s: %s", dir, f, err)
|
|
continue
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|