2024-04-02 15:47:49 -04:00
|
|
|
// Copyright (c) HashiCorp, Inc.
|
|
|
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
|
|
2024-09-16 14:28:54 -04:00
|
|
|
package host
|
2024-04-02 15:47:49 -04:00
|
|
|
|
|
|
|
|
import (
|
2024-10-28 12:50:14 -04:00
|
|
|
"context"
|
|
|
|
|
"errors"
|
|
|
|
|
"strconv"
|
2024-04-02 15:47:49 -04:00
|
|
|
"testing"
|
|
|
|
|
|
|
|
|
|
"cloud.google.com/go/compute/apiv1/computepb"
|
2024-10-28 12:50:14 -04:00
|
|
|
"github.com/hashicorp/boundary-plugin-gcp/internal/credential"
|
2024-04-02 15:47:49 -04:00
|
|
|
pb "github.com/hashicorp/boundary/sdk/pbs/plugin"
|
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func TestInstanceToHost(t *testing.T) {
|
2024-10-28 12:50:14 -04:00
|
|
|
exampleId := uint64(123456789)
|
2024-04-02 15:47:49 -04:00
|
|
|
examplePrivateIp := "10.0.0.1"
|
|
|
|
|
examplePrivateIp2 := "10.0.0.2"
|
|
|
|
|
examplePublicIp := "1.1.1.1"
|
|
|
|
|
examplePublicIp2 := "1.1.1.2"
|
|
|
|
|
exampleIPv6 := "some::fake::address"
|
2024-08-06 11:51:00 -04:00
|
|
|
exampleName := "test-instance"
|
2024-04-02 15:47:49 -04:00
|
|
|
|
|
|
|
|
cases := []struct {
|
|
|
|
|
name string
|
|
|
|
|
instance *computepb.Instance
|
|
|
|
|
expected *pb.ListHostsResponseHost
|
|
|
|
|
expectedErr string
|
|
|
|
|
}{
|
|
|
|
|
{
|
2024-10-28 12:50:14 -04:00
|
|
|
name: "missing instance id",
|
2024-08-06 11:51:00 -04:00
|
|
|
instance: &computepb.Instance{
|
|
|
|
|
Name: &exampleName,
|
|
|
|
|
},
|
2024-10-28 12:50:14 -04:00
|
|
|
expectedErr: "response integrity error: missing instance id",
|
2024-04-02 15:47:49 -04:00
|
|
|
},
|
2024-08-06 11:51:00 -04:00
|
|
|
{
|
|
|
|
|
name: "missing instance name",
|
|
|
|
|
instance: &computepb.Instance{
|
2024-10-28 12:50:14 -04:00
|
|
|
Id: &exampleId,
|
2024-08-06 11:51:00 -04:00
|
|
|
},
|
|
|
|
|
expectedErr: "response integrity error: missing instance name",
|
|
|
|
|
},
|
2024-04-02 15:47:49 -04:00
|
|
|
{
|
|
|
|
|
name: "good, single private IP with public IP address",
|
|
|
|
|
instance: &computepb.Instance{
|
2024-10-28 12:50:14 -04:00
|
|
|
Id: &exampleId,
|
|
|
|
|
Name: &exampleName,
|
2024-04-02 15:47:49 -04:00
|
|
|
NetworkInterfaces: []*computepb.NetworkInterface{
|
|
|
|
|
{
|
|
|
|
|
NetworkIP: &examplePrivateIp,
|
|
|
|
|
AccessConfigs: []*computepb.AccessConfig{
|
|
|
|
|
{
|
|
|
|
|
NatIP: &examplePublicIp,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
expected: &pb.ListHostsResponseHost{
|
2024-10-28 12:50:14 -04:00
|
|
|
ExternalId: strconv.FormatUint(exampleId, 10),
|
2024-08-06 11:51:00 -04:00
|
|
|
ExternalName: exampleName,
|
|
|
|
|
IpAddresses: []string{examplePrivateIp, examplePublicIp},
|
2024-04-02 15:47:49 -04:00
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "good, single private IP address",
|
|
|
|
|
instance: &computepb.Instance{
|
2024-10-28 12:50:14 -04:00
|
|
|
Id: &exampleId,
|
|
|
|
|
Name: &exampleName,
|
2024-04-02 15:47:49 -04:00
|
|
|
NetworkInterfaces: []*computepb.NetworkInterface{
|
|
|
|
|
{
|
|
|
|
|
NetworkIP: &examplePrivateIp,
|
|
|
|
|
AccessConfigs: []*computepb.AccessConfig{},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
expected: &pb.ListHostsResponseHost{
|
2024-10-28 12:50:14 -04:00
|
|
|
ExternalId: strconv.FormatUint(exampleId, 10),
|
2024-08-06 11:51:00 -04:00
|
|
|
ExternalName: exampleName,
|
|
|
|
|
IpAddresses: []string{examplePrivateIp},
|
2024-04-02 15:47:49 -04:00
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "good, multiple interfaces",
|
|
|
|
|
instance: &computepb.Instance{
|
2024-10-28 12:50:14 -04:00
|
|
|
Id: &exampleId,
|
|
|
|
|
Name: &exampleName,
|
2024-04-02 15:47:49 -04:00
|
|
|
NetworkInterfaces: []*computepb.NetworkInterface{
|
|
|
|
|
{
|
|
|
|
|
NetworkIP: &examplePrivateIp,
|
|
|
|
|
AccessConfigs: []*computepb.AccessConfig{
|
|
|
|
|
{
|
|
|
|
|
NatIP: &examplePublicIp,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
NetworkIP: &examplePrivateIp2,
|
|
|
|
|
AccessConfigs: []*computepb.AccessConfig{
|
|
|
|
|
{
|
|
|
|
|
NatIP: &examplePublicIp2,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
expected: &pb.ListHostsResponseHost{
|
2024-10-28 12:50:14 -04:00
|
|
|
ExternalId: strconv.FormatUint(exampleId, 10),
|
2024-08-06 11:51:00 -04:00
|
|
|
ExternalName: exampleName,
|
|
|
|
|
IpAddresses: []string{examplePrivateIp, examplePublicIp, examplePrivateIp2, examplePublicIp2},
|
2024-04-02 15:47:49 -04:00
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "good, single private IP address with IPv6",
|
|
|
|
|
instance: &computepb.Instance{
|
2024-10-28 12:50:14 -04:00
|
|
|
Id: &exampleId,
|
|
|
|
|
Name: &exampleName,
|
2024-04-02 15:47:49 -04:00
|
|
|
NetworkInterfaces: []*computepb.NetworkInterface{
|
|
|
|
|
{
|
|
|
|
|
NetworkIP: &examplePrivateIp,
|
|
|
|
|
Ipv6Address: &exampleIPv6,
|
|
|
|
|
AccessConfigs: []*computepb.AccessConfig{},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
expected: &pb.ListHostsResponseHost{
|
2024-10-28 12:50:14 -04:00
|
|
|
ExternalId: strconv.FormatUint(exampleId, 10),
|
2024-08-06 11:51:00 -04:00
|
|
|
ExternalName: exampleName,
|
|
|
|
|
IpAddresses: []string{examplePrivateIp, exampleIPv6},
|
2024-04-02 15:47:49 -04:00
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, tc := range cases {
|
|
|
|
|
tc := tc
|
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
|
|
|
require := require.New(t)
|
|
|
|
|
actual, err := instanceToHost(tc.instance)
|
|
|
|
|
if tc.expectedErr != "" {
|
|
|
|
|
require.EqualError(err, tc.expectedErr)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
require.NoError(err)
|
|
|
|
|
require.Equal(tc.expected, actual)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-10-28 12:50:14 -04:00
|
|
|
|
|
|
|
|
func TestGetInstances(t *testing.T) {
|
|
|
|
|
ctx := context.Background()
|
|
|
|
|
|
|
|
|
|
exampleId := uint64(123456789)
|
|
|
|
|
exampleName := "test-instance"
|
|
|
|
|
exampleInstance := &computepb.Instance{
|
|
|
|
|
Id: &exampleId,
|
|
|
|
|
Name: &exampleName,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cases := []struct {
|
|
|
|
|
name string
|
|
|
|
|
instancesClient InstancesAPI
|
|
|
|
|
request *computepb.ListInstancesRequest
|
|
|
|
|
expected []*computepb.Instance
|
|
|
|
|
expectedErr string
|
|
|
|
|
}{
|
|
|
|
|
{
|
|
|
|
|
name: "successful retrieval",
|
|
|
|
|
instancesClient: func() InstancesAPI {
|
|
|
|
|
fn := newTestMockInstances(ctx,
|
|
|
|
|
nil,
|
|
|
|
|
testMockInstancesWithListInstancesOutput(&computepb.InstanceList{
|
|
|
|
|
Items: []*computepb.Instance{exampleInstance},
|
|
|
|
|
}),
|
|
|
|
|
testMockInstancesWithListInstancesError(nil),
|
|
|
|
|
)
|
|
|
|
|
client, err := fn(&credential.Config{})
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
return client
|
|
|
|
|
}(),
|
|
|
|
|
request: &computepb.ListInstancesRequest{},
|
|
|
|
|
expected: []*computepb.Instance{exampleInstance},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "error during listing",
|
|
|
|
|
instancesClient: func() InstancesAPI {
|
|
|
|
|
fn := newTestMockInstances(ctx,
|
|
|
|
|
nil,
|
|
|
|
|
testMockInstancesWithListInstancesOutput(&computepb.InstanceList{}),
|
|
|
|
|
testMockInstancesWithListInstancesError(errors.New("internal error")),
|
|
|
|
|
)
|
|
|
|
|
client, err := fn(&credential.Config{})
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
return client
|
|
|
|
|
}(),
|
|
|
|
|
request: &computepb.ListInstancesRequest{},
|
|
|
|
|
expectedErr: "error listing instances",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "no instances",
|
|
|
|
|
instancesClient: func() InstancesAPI {
|
|
|
|
|
fn := newTestMockInstances(ctx,
|
|
|
|
|
nil,
|
|
|
|
|
testMockInstancesWithListInstancesOutput(&computepb.InstanceList{}),
|
|
|
|
|
testMockInstancesWithListInstancesError(nil),
|
|
|
|
|
)
|
|
|
|
|
client, err := fn(&credential.Config{})
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
return client
|
|
|
|
|
}(),
|
|
|
|
|
request: &computepb.ListInstancesRequest{},
|
|
|
|
|
expected: []*computepb.Instance{},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, tc := range cases {
|
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
|
|
|
require := require.New(t)
|
|
|
|
|
actual, err := getInstances(ctx, tc.instancesClient, tc.request)
|
|
|
|
|
if tc.expectedErr != "" {
|
|
|
|
|
require.ErrorContains(err, tc.expectedErr)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
require.NoError(err)
|
|
|
|
|
require.Len(actual, len(tc.expected))
|
|
|
|
|
if len(actual) > 0 {
|
|
|
|
|
require.Equal(tc.expected[0].Name, actual[0].Name)
|
|
|
|
|
require.Equal(tc.expected[0].Id, actual[0].Id)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|