Files
linuxkit/pkg/metadata/provider_gcp.go
Rolf Neugebauer 766e1d95d3 pkg: Add a generic metadata package
This package handles meta and user data for different cloud
and other platforms. It should be easy to extend to new
platforms.

Currently, it handles GCP metadata and a simple CDROM userdata
provider.

Signed-off-by: Rolf Neugebauer <rolf.neugebauer@docker.com>
2017-04-11 15:32:17 +01:00

106 lines
2.8 KiB
Go

package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
"path"
"strings"
"time"
)
const (
project = "http://metadata.google.internal/computeMetadata/v1/project/"
instance = "http://metadata.google.internal/computeMetadata/v1/instance/"
)
// ProviderGCP is the type implementing the Provider interface for GCP
type ProviderGCP struct {
}
// NewGCP returns a new ProviderGCP
func NewGCP() *ProviderGCP {
return &ProviderGCP{}
}
// 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)
}
// Extract gets both the GCP specific and generic userdata
func (p *ProviderGCP) Extract() ([]byte, error) {
// Get host name. This must not fail
hostname, err := gcpGet(instance + "hostname")
if err != nil {
return nil, err
}
err = ioutil.WriteFile(path.Join(ConfigPath, Hostname), hostname, 0644)
if err != nil {
return nil, fmt.Errorf("GCP: Failed to write hostname: %s", err)
}
// SSH keys:
// 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
sshKeys, err := gcpGet(project + "attributes/sshKeys")
if err == nil {
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(path.Join(ConfigPath, SSH, "authorized_keys"), []byte(rootKeys), 0600)
if err != nil {
log.Printf("GCP: Failed to write ssh keys: %s", err)
}
}
// Generic userdata
userData, err := gcpGet(instance + "attributes/userdata")
if err != nil {
log.Printf("GCP: Failed to get user-data: %s", err)
// This is not an error
return nil, nil
}
return userData, nil
}
// gcpGet requests and extracts the requested URL
func gcpGet(url string) ([]byte, error) {
var client = &http.Client{
Timeout: time.Second * 2,
}
req, err := http.NewRequest("", url, nil)
if err != nil {
return nil, fmt.Errorf("GCP: http.NewRequest failed: %s", err)
}
req.Header.Set("Metadata-Flavor", "Google")
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("GCP: Could not contact metadata service: %s", err)
}
if resp.StatusCode != 200 {
return nil, fmt.Errorf("GCP: Status not ok: %d", resp.StatusCode)
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("GCP: Failed to read http response: %s", err)
}
return body, nil
}