-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #14 from crusoecloud/v1alpha5
V1Alpha5 Support
- Loading branch information
Showing
23 changed files
with
926 additions
and
263 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
terraform { | ||
required_providers { | ||
crusoe = { | ||
source = "registry.terraform.io/crusoecloud/crusoe" | ||
} | ||
} | ||
} | ||
|
||
locals { | ||
my_ssh_key = file("~/.ssh/id_ed25519.pub") | ||
} | ||
|
||
variable "project_id" { | ||
type = string | ||
default = "<PROJECT_ID>" | ||
} | ||
|
||
// new VM | ||
resource "crusoe_compute_instance" "my_vm" { | ||
name = "my-new-vm" | ||
type = "a40.1x" | ||
location = "us-northcentral1-a" | ||
|
||
disks = [ | ||
// disk attached at startup | ||
{ | ||
id = crusoe_storage_disk.data_disk.id | ||
mode = "read-write" | ||
attachment_type = "data" | ||
} | ||
] | ||
|
||
ssh_key = local.my_ssh_key | ||
project_id = var.project_id | ||
|
||
} | ||
|
||
resource "crusoe_storage_disk" "data_disk" { | ||
name = "data-disk" | ||
size = "200GiB" | ||
project_id = var.project_id | ||
location = "us-northcentral1-a" | ||
} | ||
|
||
// firewall rule | ||
// note: this allows all ingress over TCP to our VM | ||
resource "crusoe_vpc_firewall_rule" "open_fw_rule" { | ||
network = crusoe_compute_instance.my_vm.network_interfaces[0].network | ||
name = "example-terraform-rule" | ||
action = "allow" | ||
direction = "ingress" | ||
protocols = "tcp" | ||
source = "0.0.0.0/0" | ||
source_ports = "1-65535" | ||
destination = crusoe_compute_instance.my_vm.network_interfaces[0].public_ipv4.address | ||
destination_ports = "1-65535" | ||
project_id = var.project_id | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,18 +5,18 @@ import ( | |
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"github.com/antihax/optional" | ||
"github.com/hashicorp/terraform-plugin-framework/diag" | ||
"net/http" | ||
"strings" | ||
"time" | ||
|
||
"github.com/antihax/optional" | ||
|
||
swagger "github.com/crusoecloud/client-go/swagger/v1alpha4" | ||
swagger "github.com/crusoecloud/client-go/swagger/v1alpha5" | ||
) | ||
|
||
const ( | ||
// TODO: pull from config set during build | ||
version = "v0.4.1" | ||
version = "v0.5.0" | ||
|
||
pollInterval = 2 * time.Second | ||
|
||
|
@@ -36,14 +36,11 @@ var ( | |
OpInProgress opStatus = "IN_PROGRESS" | ||
OpFailed opStatus = "FAILED" | ||
|
||
errNoOperations = errors.New("no operation with id found") | ||
errUnableToGetOpRes = errors.New("failed to get result of operation") | ||
|
||
errAmbiguousRole = errors.New("user is associated with multiple roles - please contact [email protected]") | ||
errNoRoleAssociation = errors.New("user is not associated with any role") | ||
|
||
// fallback error presented to the user in unexpected situations | ||
errUnexpected = errors.New("An unexpected error occurred. Please try again, and if the problem persists, contact [email protected].") | ||
errMultipleProjects = errors.New("User has multiple projects. Please specify a project to be used.") | ||
errUnexpected = errors.New("An unexpected error occurred. Please try again, and if the problem persists, contact [email protected].") | ||
) | ||
|
||
// NewAPIClient initializes a new Crusoe API client with the given configuration. | ||
|
@@ -60,46 +57,19 @@ func NewAPIClient(host, key, secret string) *swagger.APIClient { | |
return swagger.NewAPIClient(cfg) | ||
} | ||
|
||
// GetRole creates a get Role request and calls the API. | ||
// This function returns a role id if the user's role can be determined | ||
// (i.e. user only has one role, which is the case for v0). | ||
func GetRole(ctx context.Context, api *swagger.APIClient) (string, error) { | ||
opts := swagger.RolesApiGetRolesOpts{ | ||
OrgId: optional.EmptyString(), | ||
} | ||
|
||
resp, httpResp, err := api.RolesApi.GetRoles(ctx, &opts) | ||
if err != nil { | ||
return "", fmt.Errorf("could not get roles: %w", err) | ||
} | ||
defer httpResp.Body.Close() | ||
|
||
switch len(resp.Roles) { | ||
case 0: | ||
return "", errNoRoleAssociation | ||
case 1: | ||
return resp.Roles[0].Id, nil | ||
default: | ||
// user has multiple roles: unable to disambiguate | ||
return "", errAmbiguousRole | ||
} | ||
} | ||
|
||
// AwaitOperation polls an async API operation until it resolves into a success or failure state. | ||
func AwaitOperation(ctx context.Context, op *swagger.Operation, | ||
getFunc func(context.Context, string) (swagger.ListOperationsResponseV1Alpha4, *http.Response, error)) ( | ||
func AwaitOperation(ctx context.Context, op *swagger.Operation, projectID string, | ||
getFunc func(context.Context, string, string) (swagger.Operation, *http.Response, error)) ( | ||
*swagger.Operation, error, | ||
) { | ||
for op.State == string(OpInProgress) { | ||
updatedOps, httpResp, err := getFunc(ctx, op.OperationId) | ||
updatedOps, httpResp, err := getFunc(ctx, projectID, op.OperationId) | ||
if err != nil { | ||
return nil, fmt.Errorf("error getting operation with id %s: %w", op.OperationId, err) | ||
} | ||
httpResp.Body.Close() | ||
if len(updatedOps.Operations) == 0 { | ||
return nil, errNoOperations | ||
} | ||
op = &updatedOps.Operations[0] | ||
|
||
op = &updatedOps | ||
|
||
time.Sleep(pollInterval) | ||
} | ||
|
@@ -122,10 +92,10 @@ func AwaitOperation(ctx context.Context, op *swagger.Operation, | |
|
||
// AwaitOperationAndResolve awaits an async API operation and attempts to parse the response as an instance of T, | ||
// if the operation was successful. | ||
func AwaitOperationAndResolve[T any](ctx context.Context, op *swagger.Operation, | ||
getFunc func(context.Context, string) (swagger.ListOperationsResponseV1Alpha4, *http.Response, error), | ||
func AwaitOperationAndResolve[T any](ctx context.Context, op *swagger.Operation, projectID string, | ||
getFunc func(context.Context, string, string) (swagger.Operation, *http.Response, error), | ||
) (*T, *swagger.Operation, error) { | ||
op, err := AwaitOperation(ctx, op, getFunc) | ||
op, err := AwaitOperation(ctx, op, projectID, getFunc) | ||
if err != nil { | ||
return nil, op, err | ||
} | ||
|
@@ -138,6 +108,54 @@ func AwaitOperationAndResolve[T any](ctx context.Context, op *swagger.Operation, | |
return result, op, nil | ||
} | ||
|
||
// GetFallbackProject queries the API to get the list of projects belonging to the | ||
// logged in user. If there is one project belonging to the user, it returns that project | ||
// else it adds an error to the diagnostics and returns. | ||
func GetFallbackProject(ctx context.Context, client *swagger.APIClient, diag *diag.Diagnostics) (string, error) { | ||
|
||
config, err := GetConfig() | ||
if err != nil { | ||
return "", fmt.Errorf("failed to get config: %v", err) | ||
} | ||
|
||
var opts = &swagger.ProjectsApiListProjectsOpts{ | ||
OrgId: optional.EmptyString(), | ||
} | ||
|
||
if config.DefaultProject != "" { | ||
opts.ProjectName = optional.NewString(config.DefaultProject) | ||
} | ||
|
||
dataResp, httpResp, err := client.ProjectsApi.ListProjects(ctx, opts) | ||
|
||
defer httpResp.Body.Close() | ||
|
||
if err != nil { | ||
diag.AddError("Failed to retrieve project ID", | ||
"Failed to retrieve project ID for the authenticated user.") | ||
|
||
return "", err | ||
} | ||
|
||
if len(dataResp.Items) != 1 { | ||
diag.AddError("Multiple projects found.", | ||
"Multiple projects found for the authenticated user. Unable to determine which project to use.") | ||
|
||
return "", errMultipleProjects | ||
} | ||
|
||
projectID := dataResp.Items[0].Id | ||
|
||
if config.DefaultProject == "" { | ||
diag.AddWarning("Default project not specified", | ||
fmt.Sprintf("A project_id was not specified in the configuration file. "+ | ||
"Please specify a project in the terraform file or set a 'default_project' in your configuration file. "+ | ||
"Falling back to project: %s.", dataResp.Items[0].Name)) | ||
} | ||
|
||
return projectID, nil | ||
} | ||
|
||
func parseOpResult[T any](opResult interface{}) (*T, error) { | ||
b, err := json.Marshal(opResult) | ||
if err != nil { | ||
|
Oops, something went wrong.