Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: adds gcp metadata provider support to cloudmeta package #4155

Merged
merged 6 commits into from
Dec 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions pkg/cloudmeta/gcp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Copyright (C) 2024 ScyllaDB

package cloudmeta

import (
"context"
"strings"

"cloud.google.com/go/compute/metadata"
"github.com/pkg/errors"
)

// gcpMetadata is a wrapper around gcp metadata client.
type gcpMetadata struct {
meta *metadata.Client
}

// newGCPMetadata returns gcp metadata provider.
func newGCPMetadata() *gcpMetadata {
return &gcpMetadata{
meta: metadata.NewClient(nil),
}
}

// Metadata returns InstanceMetadata from gcp if available.
func (gcp *gcpMetadata) Metadata(ctx context.Context) (InstanceMetadata, error) {
machineType, err := gcp.getMachineType(ctx)
if err != nil {
return InstanceMetadata{}, errors.Wrap(err, "gcp.meta.GetWithContext")
}
return InstanceMetadata{
CloudProvider: CloudProviderGCP,
InstanceType: machineType,
}, nil
}

func (gcp *gcpMetadata) getMachineType(ctx context.Context) (string, error) {
machineTypeResp, err := gcp.meta.GetWithContext(ctx, "instance/machine-type")
if err != nil {
return "", errors.Wrap(err, "gcp.meta.GetWithContext")
}

machineType, err := parseMachineTypeResponse(machineTypeResp)
if err != nil {
return "", err
}

return machineType, nil
}

// The machine type for this VM. This value has the following format: projects/PROJECT_NUM/machineTypes/MACHINE_TYPE.
// See https://cloud.google.com/compute/docs/metadata/predefined-metadata-keys#instance-metadata.
func parseMachineTypeResponse(resp string) (string, error) {
errUnexpectedFormat := errors.Errorf("unexpected machineType response format: %s", resp)

parts := strings.Split(resp, "/")
if len(parts) != 4 {
return "", errUnexpectedFormat
}

if parts[2] != "machineTypes" {
return "", errUnexpectedFormat
}

return parts[3], nil
}
67 changes: 67 additions & 0 deletions pkg/cloudmeta/gcp_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright (C) 2024 ScyllaDB

package cloudmeta

import "testing"

func TestParseMachineTypeResponse(t *testing.T) {
testCases := []struct {
name string
machineTypeResponse string

expectedErr bool
expected string
}{
{
name: "everything is fine",
machineTypeResponse: "projects/project1/machineTypes/machineType1",

expectedErr: false,
expected: "machineType1",
},
{
name: "new response part is added",
machineTypeResponse: "projects/project1/zone/zone1/machineTypes/machineType1",

expectedErr: true,
expected: "",
},
{
name: "new response part is added after machineTypes part",
machineTypeResponse: "projects/project1/machineTypes/machineType1/zones/zone1",

expectedErr: true,
expected: "",
},
{
name: "parts are mixed up",
machineTypeResponse: "machineTypes/machineType1/projects/project1",

expectedErr: true,
expected: "",
},
{
name: "empty response",
machineTypeResponse: "",

expectedErr: true,
expected: "",
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
machineType, err := parseMachineTypeResponse(tc.machineTypeResponse)
if tc.expectedErr && err == nil {
t.Fatalf("expected err, but got %v", err)
}
if !tc.expectedErr && err != nil {
t.Fatalf("unexpected err: %v", err)
}

if tc.expected != machineType {
t.Fatalf("machineType(%s) != expected(%s)", machineType, tc.expected)
}
})
}
}
11 changes: 9 additions & 2 deletions pkg/cloudmeta/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,12 @@ type InstanceMetadata struct {
// CloudProvider is enum of supported cloud providers.
type CloudProvider string

// CloudProviderAWS represents aws provider.
var CloudProviderAWS CloudProvider = "aws"
const (
// CloudProviderAWS represents aws provider.
CloudProviderAWS CloudProvider = "aws"
// CloudProviderGCP represents gcp provider.
CloudProviderGCP CloudProvider = "gcp"
)

// CloudMetadataProvider interface that each metadata provider should implement.
type CloudMetadataProvider interface {
Expand All @@ -43,9 +47,12 @@ func NewCloudMeta() (*CloudMeta, error) {
return nil, err
}

gcpMeta := newGCPMetadata()

return &CloudMeta{
providers: []CloudMetadataProvider{
awsMeta,
gcpMeta,
},
providerTimeout: defaultTimeout,
}, nil
Expand Down
Loading