Skip to content
This repository has been archived by the owner on Sep 18, 2020. It is now read-only.

Commit

Permalink
Merge pull request #426 from crawford/gce
Browse files Browse the repository at this point in the history
datasource/gce: add initial support
  • Loading branch information
crawford committed May 18, 2016
2 parents 1468538 + fae31db commit 4c333e6
Show file tree
Hide file tree
Showing 9 changed files with 219 additions and 11 deletions.
11 changes: 10 additions & 1 deletion coreos-cloudinit.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (
"github.com/coreos/coreos-cloudinit/datasource/metadata/cloudsigma"
"github.com/coreos/coreos-cloudinit/datasource/metadata/digitalocean"
"github.com/coreos/coreos-cloudinit/datasource/metadata/ec2"
"github.com/coreos/coreos-cloudinit/datasource/metadata/gce"
"github.com/coreos/coreos-cloudinit/datasource/metadata/packet"
"github.com/coreos/coreos-cloudinit/datasource/proc_cmdline"
"github.com/coreos/coreos-cloudinit/datasource/url"
Expand Down Expand Up @@ -61,6 +62,7 @@ var (
waagent string
metadataService bool
ec2MetadataService string
gceMetadataService string
cloudSigmaMetadataService bool
digitalOceanMetadataService string
packetMetadataService string
Expand All @@ -86,6 +88,7 @@ func init() {
flag.StringVar(&flags.sources.waagent, "from-waagent", "", "Read data from provided waagent directory")
flag.BoolVar(&flags.sources.metadataService, "from-metadata-service", false, "[DEPRECATED - Use -from-ec2-metadata] Download data from metadata service")
flag.StringVar(&flags.sources.ec2MetadataService, "from-ec2-metadata", "", "Download EC2 data from the provided url")
flag.StringVar(&flags.sources.gceMetadataService, "from-gce-metadata", "", "Download GCE data from the provided url")
flag.BoolVar(&flags.sources.cloudSigmaMetadataService, "from-cloudsigma-metadata", false, "Download data from CloudSigma server context")
flag.StringVar(&flags.sources.digitalOceanMetadataService, "from-digitalocean-metadata", "", "Download DigitalOcean data from the provided url")
flag.StringVar(&flags.sources.packetMetadataService, "from-packet-metadata", "", "Download Packet data from metadata service")
Expand All @@ -112,6 +115,9 @@ var (
"from-ec2-metadata": "http://169.254.169.254/",
"from-configdrive": "/media/configdrive",
},
"gce": {
"from-gce-metadata": "http://metadata.google.internal/",
},
"rackspace-onmetal": {
"from-configdrive": "/media/configdrive",
"convert-netconf": "debian",
Expand Down Expand Up @@ -174,7 +180,7 @@ func main() {

dss := getDatasources()
if len(dss) == 0 {
fmt.Println("Provide at least one of --from-file, --from-configdrive, --from-ec2-metadata, --from-cloudsigma-metadata, --from-packet-metadata, --from-digitalocean-metadata, --from-vmware-guestinfo, --from-waagent, --from-url or --from-proc-cmdline")
fmt.Println("Provide at least one of --from-file, --from-configdrive, --from-ec2-metadata, --from-gce-metadata, --from-cloudsigma-metadata, --from-packet-metadata, --from-digitalocean-metadata, --from-vmware-guestinfo, --from-waagent, --from-url or --from-proc-cmdline")
os.Exit(2)
}

Expand Down Expand Up @@ -322,6 +328,9 @@ func getDatasources() []datasource.Datasource {
if flags.sources.ec2MetadataService != "" {
dss = append(dss, ec2.NewDatasource(flags.sources.ec2MetadataService))
}
if flags.sources.gceMetadataService != "" {
dss = append(dss, gce.NewDatasource(flags.sources.gceMetadataService))
}
if flags.sources.cloudSigmaMetadataService {
dss = append(dss, cloudsigma.NewServerContextService())
}
Expand Down
2 changes: 1 addition & 1 deletion datasource/metadata/digitalocean/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ type metadataService struct {
}

func NewDatasource(root string) *metadataService {
return &metadataService{MetadataService: metadata.NewDatasource(root, apiVersion, userdataUrl, metadataPath)}
return &metadataService{MetadataService: metadata.NewDatasource(root, apiVersion, userdataUrl, metadataPath, nil)}
}

func (ms *metadataService) FetchMetadata() (metadata datasource.Metadata, err error) {
Expand Down
2 changes: 1 addition & 1 deletion datasource/metadata/ec2/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ type metadataService struct {
}

func NewDatasource(root string) *metadataService {
return &metadataService{metadata.NewDatasource(root, apiVersion, userdataPath, metadataPath)}
return &metadataService{metadata.NewDatasource(root, apiVersion, userdataPath, metadataPath, nil)}
}

func (ms metadataService) FetchMetadata() (datasource.Metadata, error) {
Expand Down
89 changes: 89 additions & 0 deletions datasource/metadata/gce/metadata.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// Copyright 2016 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package gce

import (
"fmt"
"net"
"net/http"

"github.com/coreos/coreos-cloudinit/datasource"
"github.com/coreos/coreos-cloudinit/datasource/metadata"
)

const (
apiVersion = "computeMetadata/v1/"
metadataPath = apiVersion + "instance/"
userdataPath = apiVersion + "instance/attributes/user-data"
)

type metadataService struct {
metadata.MetadataService
}

func NewDatasource(root string) *metadataService {
return &metadataService{metadata.NewDatasource(root, apiVersion, userdataPath, metadataPath, http.Header{"Metadata-Flavor": {"Google"}})}
}

func (ms metadataService) FetchMetadata() (datasource.Metadata, error) {
public, err := ms.fetchIP("network-interfaces/0/access-configs/0/external-ip")
if err != nil {
return datasource.Metadata{}, err
}
local, err := ms.fetchIP("network-interfaces/0/ip")
if err != nil {
return datasource.Metadata{}, err
}
hostname, err := ms.fetchString("hostname")
if err != nil {
return datasource.Metadata{}, err
}

return datasource.Metadata{
PublicIPv4: public,
PrivateIPv4: local,
Hostname: hostname,
}, nil
}

func (ms metadataService) Type() string {
return "gce-metadata-service"
}

func (ms metadataService) fetchString(key string) (string, error) {
data, err := ms.FetchData(ms.MetadataUrl() + key)
if err != nil {
return "", err
}

return string(data), nil
}

func (ms metadataService) fetchIP(key string) (net.IP, error) {
str, err := ms.fetchString(key)
if err != nil {
return nil, err
}

if str == "" {
return nil, nil
}

if ip := net.ParseIP(str); ip != nil {
return ip, nil
} else {
return nil, fmt.Errorf("couldn't parse %q as IP address", str)
}
}
99 changes: 99 additions & 0 deletions datasource/metadata/gce/metadata_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package gce

import (
"fmt"
"net"
"reflect"
"testing"

"github.com/coreos/coreos-cloudinit/datasource"
"github.com/coreos/coreos-cloudinit/datasource/metadata"
"github.com/coreos/coreos-cloudinit/datasource/metadata/test"
"github.com/coreos/coreos-cloudinit/pkg"
)

func TestType(t *testing.T) {
want := "gce-metadata-service"
if kind := (metadataService{}).Type(); kind != want {
t.Fatalf("bad type: want %q, got %q", want, kind)
}
}

func TestFetchMetadata(t *testing.T) {
for _, tt := range []struct {
root string
metadataPath string
resources map[string]string
expect datasource.Metadata
clientErr error
expectErr error
}{
{
root: "/",
metadataPath: "computeMetadata/v1/instance/",
resources: map[string]string{},
},
{
root: "/",
metadataPath: "computeMetadata/v1/instance/",
resources: map[string]string{
"/computeMetadata/v1/instance/hostname": "host",
},
expect: datasource.Metadata{
Hostname: "host",
},
},
{
root: "/",
metadataPath: "computeMetadata/v1/instance/",
resources: map[string]string{
"/computeMetadata/v1/instance/hostname": "host",
"/computeMetadata/v1/instance/network-interfaces/0/ip": "1.2.3.4",
"/computeMetadata/v1/instance/network-interfaces/0/access-configs/0/external-ip": "5.6.7.8",
},
expect: datasource.Metadata{
Hostname: "host",
PrivateIPv4: net.ParseIP("1.2.3.4"),
PublicIPv4: net.ParseIP("5.6.7.8"),
},
},
{
clientErr: pkg.ErrTimeout{Err: fmt.Errorf("test error")},
expectErr: pkg.ErrTimeout{Err: fmt.Errorf("test error")},
},
} {
service := &metadataService{metadata.MetadataService{
Root: tt.root,
Client: &test.HttpClient{Resources: tt.resources, Err: tt.clientErr},
MetadataPath: tt.metadataPath,
}}
metadata, err := service.FetchMetadata()
if Error(err) != Error(tt.expectErr) {
t.Fatalf("bad error (%q): want %q, got %q", tt.resources, tt.expectErr, err)
}
if !reflect.DeepEqual(tt.expect, metadata) {
t.Fatalf("bad fetch (%q): want %#v, got %#v", tt.resources, tt.expect, metadata)
}
}
}

func Error(err error) string {
if err != nil {
return err.Error()
}
return ""
}
5 changes: 3 additions & 2 deletions datasource/metadata/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package metadata

import (
"net/http"
"strings"

"github.com/coreos/coreos-cloudinit/pkg"
Expand All @@ -28,11 +29,11 @@ type MetadataService struct {
MetadataPath string
}

func NewDatasource(root, apiVersion, userdataPath, metadataPath string) MetadataService {
func NewDatasource(root, apiVersion, userdataPath, metadataPath string, header http.Header) MetadataService {
if !strings.HasSuffix(root, "/") {
root += "/"
}
return MetadataService{root, pkg.NewHttpClient(), apiVersion, userdataPath, metadataPath}
return MetadataService{root, pkg.NewHttpClientHeader(header), apiVersion, userdataPath, metadataPath}
}

func (ms MetadataService) IsAvailable() bool {
Expand Down
2 changes: 1 addition & 1 deletion datasource/metadata/metadata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ func TestNewDatasource(t *testing.T) {
expectRoot: "http://169.254.169.254/",
},
} {
service := NewDatasource(tt.root, "", "", "")
service := NewDatasource(tt.root, "", "", "", nil)
if service.Root != tt.expectRoot {
t.Fatalf("bad root (%q): want %q, got %q", tt.root, tt.expectRoot, service.Root)
}
Expand Down
2 changes: 1 addition & 1 deletion datasource/metadata/packet/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ type metadataService struct {
}

func NewDatasource(root string) *metadataService {
return &metadataService{MetadataService: metadata.NewDatasource(root, apiVersion, userdataUrl, metadataPath)}
return &metadataService{MetadataService: metadata.NewDatasource(root, apiVersion, userdataUrl, metadataPath, nil)}
}

func (ms *metadataService) FetchMetadata() (metadata datasource.Metadata, err error) {
Expand Down
18 changes: 14 additions & 4 deletions pkg/http_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ type HttpClient struct {
// Maximum number of connection retries. Defaults to 15
MaxRetries int

// Whether or not to skip TLS verification. Defaults to false
SkipTLS bool
// Headers to add to the request.
Header http.Header

client *http.Client
}
Expand All @@ -74,11 +74,15 @@ type Getter interface {
}

func NewHttpClient() *HttpClient {
return NewHttpClientHeader(nil)
}

func NewHttpClientHeader(header http.Header) *HttpClient {
hc := &HttpClient{
InitialBackoff: 50 * time.Millisecond,
MaxBackoff: time.Second * 5,
MaxRetries: 15,
SkipTLS: false,
Header: header,
client: &http.Client{
Timeout: 10 * time.Second,
},
Expand Down Expand Up @@ -139,7 +143,13 @@ func (h *HttpClient) GetRetry(rawurl string) ([]byte, error) {
}

func (h *HttpClient) Get(dataURL string) ([]byte, error) {
if resp, err := h.client.Get(dataURL); err == nil {
request, err := http.NewRequest("GET", dataURL, nil)
if err != nil {
return nil, err
}

request.Header = h.Header
if resp, err := h.client.Do(request); err == nil {
defer resp.Body.Close()
switch resp.StatusCode / 100 {
case HTTP_2xx:
Expand Down

0 comments on commit 4c333e6

Please sign in to comment.