diff --git a/examples/gcp.yml b/examples/gcp.yml index e6302e1a9..5384e6700 100644 --- a/examples/gcp.yml +++ b/examples/gcp.yml @@ -26,14 +26,14 @@ onboot: - CAP_NET_RAW net: host command: ["/sbin/dhcpcd", "--nobackground", "-f", "/dhcpcd.conf", "-1"] - - name: metadata-gcp - image: "mobylinux/metadata-gcp:7fc3dd5ef92e0408fb3f76048bbaae88bbb55ad9" + - name: metadata + image: "mobylinux/metadata:5c64c2305040009891b713df22bd4bae7894c3ec" binds: - - /tmp:/etc/ssh + - /dev:/dev + - /var:/var - /tmp/etc/resolv.conf:/etc/resolv.conf readonly: true net: host - uts: host capabilities: - CAP_SYS_ADMIN services: @@ -50,7 +50,7 @@ services: net: host pid: host binds: - - /tmp/authorized_keys:/root/.ssh/authorized_keys + - /var/config/ssh/authorized_keys:/root/.ssh/authorized_keys - /tmp/etc/resolv.conf:/etc/resolv.conf - name: nginx image: "nginx:alpine" diff --git a/pkg/metadata-gcp/.gitignore b/pkg/metadata-gcp/.gitignore deleted file mode 100644 index db2b4ca32..000000000 --- a/pkg/metadata-gcp/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -dev -proc -sys -usr diff --git a/pkg/metadata-gcp/Dockerfile b/pkg/metadata-gcp/Dockerfile deleted file mode 100644 index f646e349c..000000000 --- a/pkg/metadata-gcp/Dockerfile +++ /dev/null @@ -1,3 +0,0 @@ -FROM scratch -COPY . ./ -CMD ["/usr/bin/metadata-gcp"] diff --git a/pkg/metadata-gcp/Makefile b/pkg/metadata-gcp/Makefile deleted file mode 100644 index a583a794b..000000000 --- a/pkg/metadata-gcp/Makefile +++ /dev/null @@ -1,44 +0,0 @@ -GO_COMPILE=mobylinux/go-compile:3afebc59c5cde31024493c3f91e6102d584a30b9@sha256:e0786141ea7df8ba5735b63f2a24b4ade9eae5a02b0e04c4fca33b425ec69b0a - -SHA_IMAGE=alpine:3.5@sha256:dfbd4a3a8ebca874ebd2474f044a0b33600d4523d03b0df76e5c5986cb02d7e8 - -METADATA_BINARY=usr/bin/metadata-gcp - -IMAGE=metadata-gcp - -.PHONY: tag push clean container -default: push - -$(METADATA_BINARY): gcp.go - mkdir -p $(dir $@) - tar cf - $^ | docker run --rm --net=none --log-driver=none -i $(GO_COMPILE) -o $@ | tar xf - - -DIRS=dev proc sys -$(DIRS): - mkdir -p $@ - -DEPS=$(DIRS) $(METADATA_BINARY) - -container: Dockerfile $(DEPS) - tar cf - $^ | docker build --no-cache -t $(IMAGE):build - - -hash: Dockerfile $(DEPS) - find $^ -type f | xargs cat | docker run --rm -i $(SHA_IMAGE) sha1sum - | sed 's/ .*//' > hash - -push: hash container - docker pull mobylinux/$(IMAGE):$(shell cat hash) || \ - (docker tag $(IMAGE):build mobylinux/$(IMAGE):$(shell cat hash) && \ - docker push mobylinux/$(IMAGE):$(shell cat hash)) - docker rmi $(IMAGE):build - rm -f hash - -tag: hash container - docker pull mobylinux/$(IMAGE):$(shell cat hash) || \ - docker tag $(IMAGE):build mobylinux/$(IMAGE):$(shell cat hash) - docker rmi $(IMAGE):build - rm -f hash - -clean: - rm -rf hash $(DIRS) usr - -.DELETE_ON_ERROR: diff --git a/pkg/metadata-gcp/gcp.go b/pkg/metadata-gcp/gcp.go deleted file mode 100644 index 7a1e4ad80..000000000 --- a/pkg/metadata-gcp/gcp.go +++ /dev/null @@ -1,75 +0,0 @@ -package main - -import ( - "io/ioutil" - "log" - "net/http" - "strings" - "syscall" - "time" -) - -const ( - project = "http://metadata.google.internal/computeMetadata/v1/project/" - instance = "http://metadata.google.internal/computeMetadata/v1/instance/" -) - -// If optional not set, will panic. Optional will allow 404 -// We assume most failure cases are that this code is included in a non Google Cloud -// environment, which should generally be ok, so just fail fast. -func metadata(url string, optional bool) []byte { - var client = &http.Client{ - Timeout: time.Second * 2, - } - - req, err := http.NewRequest("", url, nil) - if err != nil { - log.Fatalf("http NewRequest failed: %v", err) - } - req.Header.Set("Metadata-Flavor", "Google") - - resp, err := client.Do(req) - if err != nil { - // Probably not running on Google Cloud but this package included - log.Fatalf("Could not contact Google Cloud Metadata service: %v", err) - } - if optional && resp.StatusCode == 404 { - return []byte{} - } - if resp.StatusCode != 200 { - // Probably not running on Google Cloud but this package included - log.Fatalf("Google Cloud Metadata Server http error: %s", resp.Status) - } - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - log.Fatalf("Failed to read http response: %v", err) - } - return body -} - -func main() { - hostname := metadata(instance+"hostname", false) - err := syscall.Sethostname(hostname) - if err != nil { - log.Printf("Failed to set hostname: %v", err) - } - sshKeys := metadata(project+"attributes/sshKeys", true) - // TODO also retrieve the instance keys and respect block project keys see https://cloud.google.com/compute/docs/instances/ssh-keys - - // the keys have usernames attached, but as a simplification we are going to add them all to one root file - // TODO split them into individual user files and make the ssh container construct those users - - rootKeys := "" - for _, line := range strings.Split(string(sshKeys), "\n") { - parts := strings.SplitN(line, ":", 2) - // ignoring username for now - if len(parts) == 2 { - rootKeys = rootKeys + parts[1] + "\n" - } - } - - err = ioutil.WriteFile("/etc/ssh/authorized_keys", []byte(rootKeys), 0600) - if err != nil { - log.Printf("Failed to write ssh keys: %v", err) - } -} diff --git a/pkg/metadata/main.go b/pkg/metadata/main.go index 52adcb952..b6de5918b 100644 --- a/pkg/metadata/main.go +++ b/pkg/metadata/main.go @@ -29,6 +29,9 @@ const ( // 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 @@ -53,11 +56,13 @@ func main() { log.Fatalf("Could not create %s: %s", ConfigPath, err) } + var p Provider var userdata []byte var err error found := false - for _, p := range netProviders { + for _, p = range netProviders { if p.Probe() { + log.Printf("%s: Probe succeeded", p) userdata, err = p.Extract() found = true break @@ -76,8 +81,9 @@ func main() { defer syscall.Unmount(MountPoint, 0) // Don't worry about removing MountPoint. We are in a container - for _, p := range cdromProviders { + for _, p = range cdromProviders { if p.Probe() { + log.Printf("%s: Probe succeeded", p) userdata, err = p.Extract() found = true break @@ -95,6 +101,11 @@ ErrorOut: 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) @@ -139,16 +150,28 @@ func processUserData(data []byte) error { // This is not an error return nil } - cm := fd.(map[string]interface{}) + 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.Mkdir(dir, 0755); err != nil { log.Printf("Failed to create %s: %s", dir, err) continue } - files := val.(map[string]interface{}) + files, ok := val.(map[string]interface{}) + if !ok { + log.Printf("Could convert JSON for files: %s", val) + continue + } for f, i := range files { fi := i.(map[string]interface{}) + if !ok { + log.Printf("Could convert JSON for items: %s", i) + continue + } if _, ok := fi["perm"]; !ok { log.Printf("No permission provided %s:%s", f, fi) continue diff --git a/pkg/metadata/provider_cdrom.go b/pkg/metadata/provider_cdrom.go index 90c45970e..e28951f24 100644 --- a/pkg/metadata/provider_cdrom.go +++ b/pkg/metadata/provider_cdrom.go @@ -3,7 +3,6 @@ package main import ( "fmt" "io/ioutil" - "log" "os" "path" ) @@ -22,12 +21,13 @@ func NewCDROM() *ProviderCDROM { return &ProviderCDROM{} } +func (p *ProviderCDROM) String() string { + return "CDROM" +} + // Probe checks if the CD has the right file func (p *ProviderCDROM) Probe() bool { _, err := os.Stat(path.Join(MountPoint, configFile)) - if err != nil { - log.Printf("CDROM: Probe -> %s", err) - } return (!os.IsNotExist(err)) } diff --git a/pkg/metadata/provider_gcp.go b/pkg/metadata/provider_gcp.go index 180c98986..6aa3ae359 100644 --- a/pkg/metadata/provider_gcp.go +++ b/pkg/metadata/provider_gcp.go @@ -5,6 +5,7 @@ import ( "io/ioutil" "log" "net/http" + "os" "path" "strings" "time" @@ -24,11 +25,14 @@ func NewGCP() *ProviderGCP { return &ProviderGCP{} } +func (p *ProviderGCP) String() string { + return "GCP" +} + // Probe checks if we are running on GCP func (p *ProviderGCP) Probe() bool { // Getting the hostname should always work... _, err := gcpGet(instance + "hostname") - log.Printf("GCP: Probe -> %s", err) return (err == nil) } @@ -54,6 +58,10 @@ func (p *ProviderGCP) Extract() ([]byte, error) { // container construct those users sshKeys, err := gcpGet(project + "attributes/sshKeys") if err == nil { + if err := os.Mkdir(path.Join(ConfigPath, SSH), 0755); err != nil { + log.Printf("Failed to create %s: %s", SSH, err) + goto ErrorSSH + } rootKeys := "" for _, line := range strings.Split(string(sshKeys), "\n") { parts := strings.SplitN(line, ":", 2) @@ -68,6 +76,7 @@ func (p *ProviderGCP) Extract() ([]byte, error) { } } +ErrorSSH: // Generic userdata userData, err := gcpGet(instance + "attributes/userdata") if err != nil { diff --git a/projects/demo/etcd/etcd.yml b/projects/demo/etcd/etcd.yml index c14103dc3..2cfa1938a 100644 --- a/projects/demo/etcd/etcd.yml +++ b/projects/demo/etcd/etcd.yml @@ -43,14 +43,15 @@ onboot: net: host command: ["/sbin/dhcpcd", "--nobackground", "-f", "/dhcpcd.conf", "-1"] - name: metadata - image: mobylinux/metadata:960e6c371c9a07cf512dfcfa9734a5e0db427e96 - net: host + image: "mobylinux/metadata:5c64c2305040009891b713df22bd4bae7894c3ec" binds: - - /dev:/dev - - /var:/var - - /etc/resolv.conf:/etc/resolv.conf + - /dev:/dev + - /var:/var + - /tmp/etc/resolv.conf:/etc/resolv.conf + readonly: true + net: host capabilities: - - all + - CAP_SYS_ADMIN services: - name: rngd image: "mobylinux/rngd:3dad6dd43270fa632ac031e99d1947f20b22eec9"