mirror of
				https://github.com/linuxkit/linuxkit.git
				synced 2025-10-31 11:45:55 +00:00 
			
		
		
		
	Use os.Mkdirall() instead of os.Mkdir() as it does not error if the path already exists. This enables specifying a default config file with the image and then over writing it with metadata. While at it, also update to the latest alpine base image. Signed-off-by: Rolf Neugebauer <rolf.neugebauer@docker.com>
		
			
				
	
	
		
			211 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			211 lines
		
	
	
		
			5.1 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"
 | |
| 
 | |
| 	// MountPoint is where the CDROM is mounted
 | |
| 	MountPoint = "/cdrom"
 | |
| 
 | |
| 	// 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"
 | |
| 
 | |
| 	// TODO(rneugeba): Need to check this is the same everywhere
 | |
| 	cdromDev = "/dev/sr0"
 | |
| )
 | |
| 
 | |
| // 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 = []Provider{NewCDROM()}
 | |
| }
 | |
| 
 | |
| 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 {
 | |
| 		log.Printf("Trying CDROM")
 | |
| 		if err := os.MkdirAll(MountPoint, 0755); err != nil {
 | |
| 			log.Printf("CDROM: Failed to create %s: %s", MountPoint, err)
 | |
| 			goto ErrorOut
 | |
| 		}
 | |
| 		if err := mountCDROM(cdromDev, MountPoint); err != nil {
 | |
| 			log.Printf("Failed to mount cdrom: %s", err)
 | |
| 			goto ErrorOut
 | |
| 		}
 | |
| 		defer syscall.Unmount(MountPoint, 0)
 | |
| 		// Don't worry about removing MountPoint. We are in a container
 | |
| 
 | |
| 		for _, p = range cdromProviders {
 | |
| 			if p.Probe() {
 | |
| 				log.Printf("%s: Probe succeeded", p)
 | |
| 				userdata, err = p.Extract()
 | |
| 				found = true
 | |
| 				break
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| ErrorOut:
 | |
| 	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:%s", f, fi)
 | |
| 					continue
 | |
| 				}
 | |
| 				if _, ok := fi["content"]; !ok {
 | |
| 					log.Printf("No content provided %s:%s", f, fi)
 | |
| 					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
 | |
| }
 | |
| 
 | |
| // mountCDROM mounts a CDROM/DVD device under mountPoint
 | |
| func mountCDROM(device, mountPoint string) error {
 | |
| 	// We may need to poll a little for device ready
 | |
| 	return syscall.Mount(device, mountPoint, "iso9660", syscall.MS_RDONLY, "")
 | |
| }
 |