Skip to content

Commit

Permalink
feat: instances support
Browse files Browse the repository at this point in the history
- `api` config integration

Signed-off-by: Tobias Cudnik <[email protected]>
  • Loading branch information
TobiaszCudnik committed Sep 15, 2023
1 parent 207c35a commit d88d3d1
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 66 deletions.
4 changes: 2 additions & 2 deletions internal/config/from_file.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ func (c *fileConfig) Hosts() ([]string, error) {
hostnames = append(hostnames, entry.Host)
}

sort.SliceStable(hostnames, func(i, j int) bool { return hostnames[i] == instance.Default() })
sort.SliceStable(hostnames, func(i, j int) bool { return hostnames[i] == instance.FallbackHostname() })

return hostnames, nil
}
Expand Down Expand Up @@ -237,7 +237,7 @@ func (c *fileConfig) HostsTyped() ([]HostConfigTyped, error) {
func (c *fileConfig) DefaultHostname() string {
hostname, err := c.Get("", "default_hostname")
if err != nil {
return instance.Default()
return instance.FallbackHostname()
}
return hostname
}
Expand Down
36 changes: 23 additions & 13 deletions internal/instance/host.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,39 @@ import (
"strings"
)

// Default returns the host name of the default Instill instance
func Default() string {
// FallbackHostname returns the host name of the default Instill Cloud instance.
func FallbackHostname() string {
return "api.instill.tech"
}

// ExtractHostname returns the canonical host name of a Instill instance
// TODO replace with config.HostConfigTyped.APIHostname
func ExtractHostname(h string) string {
hostname := strings.ToLower(h)

parts := strings.Split(hostname, ".")
return parts[len(parts)-2] + "." + parts[len(parts)-1]
}

// HostnameValidator validates a hostname
// HostnameValidator validates a hostname, with an optional port number.
// TODO move to utils
func HostnameValidator(v string) error {
host := struct {
name string `validate:"required,hostname"`
// without a port
host1 := struct {
Name string `validate:"required,hostname"`
}{
name: v,
Name: v,
}
err := validator.New(validator.WithRequiredStructEnabled()).Struct(host)
if err != nil {
return err
err1 := validator.New().Struct(&host1)
// with a port
host2 := struct {
Name string `validate:"required,hostname_port"`
}{
Name: v,
}
err2 := validator.New().Struct(&host2)
if err1 != nil && err2 != nil {
return fmt.Errorf("hostname not valid")
}
return nil
}
Expand All @@ -38,13 +47,14 @@ func HostnameValidator(v string) error {
// TODO remove
func RESTPrefix(hostname string) string {
if strings.EqualFold(hostname, "localhost") {
return fmt.Sprintf("http://api.%s/", hostname)
return fmt.Sprintf("http://%s/", hostname)
}
return fmt.Sprintf("https://api.%s/", hostname)
return fmt.Sprintf("https://%s/", hostname)
}

// HostPrefix sets the prefix of Instill domain
func HostPrefix(hostname string) string {
// GetProtocol returns the correct protocol based on a hostname
func GetProtocol(hostname string) string {
// TODO support port numbers
if strings.HasSuffix(hostname, "localhost") {
return fmt.Sprintf("http://%s/", hostname)
}
Expand Down
43 changes: 10 additions & 33 deletions internal/instance/host_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func TestNormalizeHostname(t *testing.T) {
func TestHostnameValidator(t *testing.T) {
tests := []struct {
name string
input interface{}
input string
wantsErr bool
}{
{
Expand All @@ -57,23 +57,23 @@ func TestHostnameValidator(t *testing.T) {
wantsErr: false,
},
{
name: "hostname with slashes",
input: "//internal.instance",
wantsErr: true,
name: "port number",
input: "hostname:123",
wantsErr: false,
},
{
name: "empty hostname",
input: " ",
name: "empty",
input: "",
wantsErr: true,
},
{
name: "hostname with colon",
input: "internal.instance:2205",
name: "hostname with slashes",
input: "//internal.instance",
wantsErr: true,
},
{
name: "non-string hostname",
input: 62,
name: "whitespace",
input: " ",
wantsErr: true,
},
}
Expand All @@ -89,26 +89,3 @@ func TestHostnameValidator(t *testing.T) {
})
}
}

func TestRESTPrefix(t *testing.T) {
tests := []struct {
host string
want string
}{
{
host: "instill.tech",
want: "https://api.instill.tech/",
},
{
host: "instill.localhost",
want: "http://api.instill.localhost/",
},
}
for _, tt := range tests {
t.Run(tt.host, func(t *testing.T) {
if got := RESTPrefix(tt.host); got != tt.want {
t.Errorf("RESTPrefix() = %v, want %v", got, tt.want)
}
})
}
}
34 changes: 25 additions & 9 deletions pkg/cmd/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ func NewCmdApi(f *cmdutil.Factory, runF func(*ApiOptions) error) *cobra.Command
Config: f.Config,
HTTPClient: f.HTTPClient,
}
// TODO handle error
cfg, _ := opts.Config()

cmd := &cobra.Command{
Use: "api <endpoint>",
Expand Down Expand Up @@ -138,7 +140,7 @@ func NewCmdApi(f *cmdutil.Factory, runF func(*ApiOptions) error) *cobra.Command
},
}

cmd.Flags().StringVar(&opts.Hostname, "instance", "", "Override currently selected instance")
cmd.Flags().StringVar(&opts.Hostname, "hostname", cfg.DefaultHostname(), "Target instance")
cmd.Flags().StringVarP(&opts.RequestMethod, "method", "X", "GET", "The HTTP method for the request")
cmd.Flags().StringArrayVarP(&opts.MagicFields, "field", "F", nil, "Add a typed parameter in `key=value` format")
cmd.Flags().StringArrayVarP(&opts.RawFields, "raw-field", "f", nil, "Add a string parameter in `key=value` format")
Expand Down Expand Up @@ -199,32 +201,46 @@ func apiRun(opts *ApiOptions) error {
defer opts.IO.StopPager()
}

// get the host config
cfg, err := opts.Config()
if err != nil {
return err
}

host := cfg.DefaultHostname()
var host *config.HostConfigTyped
if err != nil {
return err
}
hosts, err := cfg.HostsTyped()
if err != nil {
return err
}
for i := range hosts {
if hosts[i].APIHostname == opts.Hostname {
host = &hosts[i]
break
}
}
if host == nil {
return fmt.Errorf("instance '%s' doesn't exist", opts.Hostname)
}

if opts.Hostname != "" {
host = opts.Hostname
// set up the request
// TODO support other services than VDP
requestPath = "vdp/" + host.APIVersion + "/" + strings.TrimPrefix(requestPath, "/")
if host.AccessToken != "" {
requestHeaders = append(requestHeaders, "Authorization: Bearer "+host.AccessToken)
}

// http request & output
template := export.NewTemplate(opts.IO, opts.Template)

resp, err := httpRequest(httpClient, host, method, requestPath, requestBody, requestHeaders)
resp, err := httpRequest(httpClient, host.APIHostname, method, requestPath, requestBody, requestHeaders)
if err != nil {
return err
}

err = processResponse(resp, opts, headersOutputStream, &template)
if err != nil {
return err
}

return template.End()
}

Expand Down
12 changes: 3 additions & 9 deletions pkg/cmd/api/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,17 @@ import (
"bytes"
"encoding/json"
"fmt"
"github.com/instill-ai/cli/internal/instance"
"io"
"net/http"
"net/url"
"strconv"
"strings"

"github.com/instill-ai/cli/internal/instance"
)

func httpRequest(client *http.Client, hostname string, method string, p string, params interface{}, headers []string) (*http.Response, error) {
func httpRequest(client *http.Client, hostname string, method string, path string, params interface{}, headers []string) (*http.Response, error) {

var requestURL string
if strings.HasPrefix(p, "https://") {
requestURL = p
} else {
requestURL = instance.RESTPrefix(hostname) + strings.TrimPrefix(p, "/")
}
requestURL := instance.GetProtocol(hostname) + strings.TrimPrefix(path, "/")

var body io.Reader
var bodyIsJSON bool
Expand Down

0 comments on commit d88d3d1

Please sign in to comment.