Skip to content

Commit

Permalink
Added client metal-go
Browse files Browse the repository at this point in the history
  • Loading branch information
codinja1188 committed Jun 12, 2023
1 parent baa9ce0 commit 9d02f8f
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 24 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/equinix/metal-cli
go 1.19

require (
github.com/equinix-labs/metal-go v0.7.1
github.com/manifoldco/promptui v0.9.0
github.com/olekukonko/tablewriter v0.0.5
github.com/packethost/packngo v0.29.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/equinix-labs/metal-go v0.7.1 h1:dcLkBhPlTn2v4VJJBDZorYLo3QUs10FxQnwKJ8IczHk=
github.com/equinix-labs/metal-go v0.7.1/go.mod h1:qVHxbntL4uTflH4+OhtM7MNawG8j3swquGh3LLLPdfw=
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
Expand Down
44 changes: 42 additions & 2 deletions internal/cli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"runtime"
"strings"

metal "github.com/equinix-labs/metal-go/metal/v1"
"github.com/packethost/packngo"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
Expand All @@ -40,11 +41,13 @@ import (
const (
envPrefix = "METAL"
configFileWithoutExtension = "metal"
debugVar = "PACKNGO_DEBUG"
)

type Client struct {
// apiClient client
apiClient *packngo.Client
apiClient *packngo.Client
metalApiClient *metal.APIClient

includes *[]string // nolint:unused
excludes *[]string // nolint:unused
Expand Down Expand Up @@ -83,16 +86,33 @@ func NewClient(consumerToken, apiURL, Version string) *Client {
}
}

// This function provides backwards compatibility for the packngo
// debug environment variable while allowing us to introduce a new
// debug variable in the future that is not tied to packngo
func checkEnvForDebug() bool {
return os.Getenv(debugVar) != ""
}

func (c *Client) apiConnect(httpClient *http.Client) error {
client, err := packngo.NewClientWithBaseURL(c.consumerToken, c.metalToken, httpClient, c.apiURL)
if err != nil {
return fmt.Errorf("Could not create Client: %w", err)
return fmt.Errorf("could not create client: %w", err)
}
client.UserAgent = fmt.Sprintf("metal-cli/%s %s", c.Version, client.UserAgent)
c.apiClient = client
return nil
}

func (c *Client) metalApiConnect(httpClient *http.Client) error {
configuration := metal.NewConfiguration()
configuration.Debug = checkEnvForDebug()
configuration.AddDefaultHeader("X-Auth-Token", c.Token())
configuration.UserAgent = fmt.Sprintf("metal-cli/%s %s", c.Version, configuration.UserAgent)
metalgoClient := metal.NewAPIClient(configuration)
c.metalApiClient = metalgoClient
return nil
}

func (c *Client) Config(cmd *cobra.Command) *viper.Viper {
if c.viper == nil {
v := viper.New()
Expand Down Expand Up @@ -169,6 +189,26 @@ func (c *Client) API(cmd *cobra.Command) *packngo.Client {
return c.apiClient
}

func (c *Client) MetalAPI(cmd *cobra.Command) *metal.APIClient {
if c.metalToken == "" {
log.Fatal("Equinix Metal authentication token not provided. Please set the 'METAL_AUTH_TOKEN' environment variable or create a configuration file using 'metal init'.")
}

if c.metalApiClient == nil {
httpClient := &http.Client{
Transport: &headerTransport{
header: getAdditionalHeaders(cmd),
},
}

err := c.metalApiConnect(httpClient)
if err != nil {
log.Fatal(err)
}
}
return c.metalApiClient
}

func (c *Client) Token() string {
return c.metalToken
}
Expand Down
66 changes: 44 additions & 22 deletions internal/init/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,22 @@ THE SOFTWARE.
package init

import (
"context"
"fmt"
"os"
"path/filepath"
"syscall"

"github.com/packethost/packngo"
metal "github.com/equinix-labs/metal-go/metal/v1"
"github.com/spf13/cobra"
"golang.org/x/term"
"sigs.k8s.io/yaml"
)

type Client struct {
Servicer Servicer
UserService packngo.UserService
ProjectService packngo.ProjectService
UserService metal.UsersApiService
ProjectService metal.ProjectsApiService
}

func NewClient(s Servicer) *Client {
Expand Down Expand Up @@ -85,25 +86,26 @@ func (c *Client) NewCommand() *cobra.Command {
fmt.Println()
token := string(b)
c.Servicer.SetToken(token)
metalClient := c.Servicer.API(cmd)
c.UserService = metalClient.Users
c.ProjectService = metalClient.Projects
metalGoClient := c.Servicer.MetalAPI(cmd)
c.UserService = *metalGoClient.UsersApi
c.ProjectService = *metalGoClient.ProjectsApi

user, _, err := c.UserService.Current()
user, _, err := c.UserService.FindCurrentUser(context.Background()).Execute()
if err != nil {
return err
}
organization := user.DefaultOrganizationID
project := ""
if user.DefaultProjectID != nil {
project = *user.DefaultProjectID
}
organization := user.AdditionalProperties["default_organization_id"]
project := fmt.Sprintf("%v", user.AdditionalProperties["default_project_id"])
fmt.Printf("Organization ID [%s]: ", organization)

defaultOrganizationId := fmt.Sprintf("%v", organization)

userOrg := ""
fmt.Scanln(&userOrg)
if userOrg == "" {
userOrg = organization
if defaultOrganizationId != "" {
userOrg = defaultOrganizationId
}
}

// Choose the first project in the preferred org
Expand All @@ -113,6 +115,7 @@ func (c *Client) NewCommand() *cobra.Command {
return err
}
}

fmt.Printf("Project ID [%s]: ", project)

userProj := ""
Expand All @@ -132,18 +135,38 @@ func (c *Client) NewCommand() *cobra.Command {
return initCmd
}

func getFirstProjectID(s packngo.ProjectService, userOrg string) (string, error) {
listOpts := &packngo.ListOptions{}
listOpts.Including("organization")
listOpts.Excluding("devices", "members", "memberships", "invitations", "max_devices", "ssh_keys", "volumes", "backend_transfer_enabled", "updated_at", "customdata", "event_alert_configuration")
func getAllProjects(s metal.ProjectsApiService) ([]metal.Project, error) {
var projects []metal.Project

include := []string{"organization"} // []string | Nested attributes to include. Included objects will return their full attributes. Attribute names can be dotted (up to 3 levels) to included deeply nested objects. (optional)
exclude := []string{"devices", "members", "memberships", "invitations", "ssh_keys", "volumes", "backend_transfer_enabled", "updated_at", "customdata", "event_alert_configuration"}
page := int32(1) // int32 | Page to return (optional) (default to 1)
perPage := int32(56) // int32 | Items returned per page (optional) (default to 10)
for {
projectPage, _, err := s.FindProjects(context.Background()).Include(include).Exclude(exclude).Page(page).PerPage(perPage).Execute()
if err != nil {
return nil, err
}
projects = append(projects, projectPage.GetProjects()...)
if projectPage.Meta.GetLastPage() > projectPage.Meta.GetCurrentPage() {
page = page + 1
continue
}
return projects, nil
}
}

projects, _, err := s.List(listOpts)
func getFirstProjectID(s metal.ProjectsApiService, userOrg string) (string, error) {
projects, err := getAllProjects(s)
if err != nil {
return "", err
}

for _, p := range projects {
if p.Organization.ID == userOrg {
return p.ID, nil
organization := p.AdditionalProperties["organization"].(map[string]interface{})
organization_id := fmt.Sprintf("%v", organization["id"])
if organization_id == userOrg {
return p.GetId(), nil
}
}

Expand All @@ -170,8 +193,7 @@ func writeConfig(config string, b []byte) error {
}

type Servicer interface {
API(*cobra.Command) *packngo.Client
ListOptions(defaultIncludes, defaultExcludes []string) *packngo.ListOptions
MetalAPI(*cobra.Command) *metal.APIClient
SetToken(string)
DefaultConfig(bool) string
}

0 comments on commit 9d02f8f

Please sign in to comment.