linuxkit/pkg/metadata/main.go
Ian Campbell 826ee28c8d metadata: print Entry objects with %+v not %s
go_vet (via https://goreportcard.com/report/github.com/linuxkit/linuxkit)
reported:

    error: arg current for printf verb %s of wrong type: main.Entry (vet)

For each of these.

Signed-off-by: Ian Campbell <ijc@docker.com>
2018-01-15 17:24:36 +00:00

221 lines
5.5 KiB
Go

package main
import (
"encoding/json"
"io/ioutil"
"log"
"os"
"path"
"strconv"
"syscall"
)
const (
// ConfigPath is where the data is extracted to
ConfigPath = "/run/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"
// Standard AWS-compatible Metadata URLs
userDataURL = "http://169.254.169.254/latest/user-data"
metaDataURL = "http://169.254.169.254/latest/meta-data/"
)
// 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 main() {
providers := []string{"aws", "gcp", "openstack", "vultr", "packet", "cdrom"}
if len(os.Args) > 1 {
providers = os.Args[1:]
}
for _, p := range providers {
switch p {
case "aws":
netProviders = append(netProviders, NewAWS())
case "gcp":
netProviders = append(netProviders, NewGCP())
case "openstack":
netProviders = append(netProviders, NewOpenstack())
case "packet":
netProviders = append(netProviders, NewPacket())
case "vultr":
netProviders = append(netProviders, NewVultr())
case "cdrom":
cdromProviders = ListCDROMs()
default:
log.Fatalf("Unrecognised metadata provider: %s", p)
}
}
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(ConfigPath, 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(basePath string, data []byte) error {
// Always write the raw data to a file
err := ioutil.WriteFile(path.Join(basePath, "userdata"), data, 0644)
if err != nil {
log.Printf("Could not write userdata: %s", err)
return err
}
var root ConfigFile
if err := json.Unmarshal(data, &root); err != nil {
// Userdata is no JSON, presumably...
log.Printf("Could not unmarshall userdata: %s", err)
// This is not an error
return nil
}
for dir, entry := range root {
writeConfigFiles(path.Join(basePath, dir), entry)
}
return nil
}
func writeConfigFiles(target string, current Entry) {
if isFile(current) {
filemode, err := parseFileMode(current.Perm, 0644)
if err != nil {
log.Printf("Failed to parse permission %+v: %s", current, err)
return
}
if err := ioutil.WriteFile(target, []byte(*current.Content), filemode); err != nil {
log.Printf("Failed to write %s: %s", target, err)
return
}
} else if isDirectory(current) {
filemode, err := parseFileMode(current.Perm, 0755)
if err != nil {
log.Printf("Failed to parse permission %+v: %s", current, err)
return
}
if err := os.MkdirAll(target, filemode); err != nil {
log.Printf("Failed to create %s: %s", target, err)
return
}
for dir, entry := range current.Entries {
writeConfigFiles(path.Join(target, dir), entry)
}
} else {
log.Printf("%s is invalid", target)
}
}
func isFile(json Entry) bool {
return json.Content != nil && json.Entries == nil
}
func isDirectory(json Entry) bool {
return json.Content == nil && json.Entries != nil
}
func parseFileMode(input string, defaultMode os.FileMode) (os.FileMode, error) {
if input != "" {
perm, err := strconv.ParseUint(input, 8, 32)
if err != nil {
return 0, err
}
return os.FileMode(perm), nil
}
return defaultMode, nil
}
// ConfigFile represents the configuration file
type ConfigFile map[string]Entry
// Entry represents either a directory or a file
type Entry struct {
Perm string `json:"perm,omitempty"`
Content *string `json:"content,omitempty"`
Entries map[string]Entry `json:"entries,omitempty"`
}