Skip to content
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
9 changes: 9 additions & 0 deletions cmd/browsers.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ type BrowsersCreateInput struct {
ProfileID string
ProfileName string
ProfileSaveChanges BoolFlag
ProxyID string
}

type BrowsersDeleteInput struct {
Expand Down Expand Up @@ -178,6 +179,11 @@ func (b BrowsersCmd) Create(ctx context.Context, in BrowsersCreateInput) error {
}
}

// Add proxy if specified
if in.ProxyID != "" {
params.ProxyID = kernel.Opt(in.ProxyID)
}

browser, err := b.browsers.New(ctx, params)
if err != nil {
return util.CleanedUpSdkError{Err: err}
Expand Down Expand Up @@ -1321,6 +1327,7 @@ func init() {
browsersCreateCmd.Flags().String("profile-id", "", "Profile ID to load into the browser session (mutually exclusive with --profile-name)")
browsersCreateCmd.Flags().String("profile-name", "", "Profile name to load into the browser session (mutually exclusive with --profile-id)")
browsersCreateCmd.Flags().Bool("save-changes", false, "If set, save changes back to the profile when the session ends")
browsersCreateCmd.Flags().String("proxy-id", "", "Proxy ID to use for the browser session")

// Add flags for delete command
browsersDeleteCmd.Flags().BoolP("yes", "y", false, "Skip confirmation prompt")
Expand All @@ -1346,6 +1353,7 @@ func runBrowsersCreate(cmd *cobra.Command, args []string) error {
profileID, _ := cmd.Flags().GetString("profile-id")
profileName, _ := cmd.Flags().GetString("profile-name")
saveChanges, _ := cmd.Flags().GetBool("save-changes")
proxyID, _ := cmd.Flags().GetString("proxy-id")

in := BrowsersCreateInput{
PersistenceID: persistenceID,
Expand All @@ -1355,6 +1363,7 @@ func runBrowsersCreate(cmd *cobra.Command, args []string) error {
ProfileID: profileID,
ProfileName: profileName,
ProfileSaveChanges: BoolFlag{Set: cmd.Flags().Changed("save-changes"), Value: saveChanges},
ProxyID: proxyID,
}

svc := client.Browsers
Expand Down
110 changes: 110 additions & 0 deletions cmd/proxies/common_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package proxies

import (
"bytes"
"context"
"os"
"testing"

"github.com/onkernel/kernel-go-sdk"
"github.com/onkernel/kernel-go-sdk/option"
"github.com/pterm/pterm"
)

// captureOutput sets pterm writers for tests
func captureOutput(t *testing.T) *bytes.Buffer {
var buf bytes.Buffer
pterm.SetDefaultOutput(&buf)
pterm.Info.Writer = &buf
pterm.Error.Writer = &buf
pterm.Success.Writer = &buf
pterm.Warning.Writer = &buf
pterm.Debug.Writer = &buf
pterm.Fatal.Writer = &buf
pterm.DefaultTable = *pterm.DefaultTable.WithWriter(&buf)
t.Cleanup(func() {
pterm.SetDefaultOutput(os.Stdout)
pterm.Info.Writer = os.Stdout
pterm.Error.Writer = os.Stdout
pterm.Success.Writer = os.Stdout
pterm.Warning.Writer = os.Stdout
pterm.Debug.Writer = os.Stdout
pterm.Fatal.Writer = os.Stdout
pterm.DefaultTable = *pterm.DefaultTable.WithWriter(os.Stdout)
})
return &buf
}

// FakeProxyService implements ProxyService for testing
type FakeProxyService struct {
ListFunc func(ctx context.Context, opts ...option.RequestOption) (*[]kernel.ProxyListResponse, error)
GetFunc func(ctx context.Context, id string, opts ...option.RequestOption) (*kernel.ProxyGetResponse, error)
NewFunc func(ctx context.Context, body kernel.ProxyNewParams, opts ...option.RequestOption) (*kernel.ProxyNewResponse, error)
DeleteFunc func(ctx context.Context, id string, opts ...option.RequestOption) error
}

func (f *FakeProxyService) List(ctx context.Context, opts ...option.RequestOption) (*[]kernel.ProxyListResponse, error) {
if f.ListFunc != nil {
return f.ListFunc(ctx, opts...)
}
empty := []kernel.ProxyListResponse{}
return &empty, nil
}

func (f *FakeProxyService) Get(ctx context.Context, id string, opts ...option.RequestOption) (*kernel.ProxyGetResponse, error) {
if f.GetFunc != nil {
return f.GetFunc(ctx, id, opts...)
}
return &kernel.ProxyGetResponse{ID: id, Type: kernel.ProxyGetResponseTypeDatacenter}, nil
}

func (f *FakeProxyService) New(ctx context.Context, body kernel.ProxyNewParams, opts ...option.RequestOption) (*kernel.ProxyNewResponse, error) {
if f.NewFunc != nil {
return f.NewFunc(ctx, body, opts...)
}
return &kernel.ProxyNewResponse{ID: "new-proxy", Type: kernel.ProxyNewResponseTypeDatacenter}, nil
}

func (f *FakeProxyService) Delete(ctx context.Context, id string, opts ...option.RequestOption) error {
if f.DeleteFunc != nil {
return f.DeleteFunc(ctx, id, opts...)
}
return nil
}

// Helper function to create test proxy responses
func createDatacenterProxy(id, name, country string) kernel.ProxyListResponse {
return kernel.ProxyListResponse{
ID: id,
Name: name,
Type: kernel.ProxyListResponseTypeDatacenter,
Config: kernel.ProxyListResponseConfigUnion{
Country: country,
},
}
}

func createResidentialProxy(id, name, country, city, state string) kernel.ProxyListResponse {
return kernel.ProxyListResponse{
ID: id,
Name: name,
Type: kernel.ProxyListResponseTypeResidential,
Config: kernel.ProxyListResponseConfigUnion{
Country: country,
City: city,
State: state,
},
}
}

func createCustomProxy(id, name, host string, port int64) kernel.ProxyListResponse {
return kernel.ProxyListResponse{
ID: id,
Name: name,
Type: kernel.ProxyListResponseTypeCustom,
Config: kernel.ProxyListResponseConfigUnion{
Host: host,
Port: port,
},
}
}
210 changes: 210 additions & 0 deletions cmd/proxies/create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
package proxies

import (
"context"
"fmt"

"github.com/onkernel/cli/pkg/util"
"github.com/onkernel/kernel-go-sdk"
"github.com/pterm/pterm"
"github.com/spf13/cobra"
)

func (p ProxyCmd) Create(ctx context.Context, in ProxyCreateInput) error {
// Validate proxy type
var proxyType kernel.ProxyNewParamsType
switch in.Type {
case "datacenter":
proxyType = kernel.ProxyNewParamsTypeDatacenter
case "isp":
proxyType = kernel.ProxyNewParamsTypeIsp
case "residential":
proxyType = kernel.ProxyNewParamsTypeResidential
case "mobile":
proxyType = kernel.ProxyNewParamsTypeMobile
case "custom":
proxyType = kernel.ProxyNewParamsTypeCustom
default:
return fmt.Errorf("invalid proxy type: %s", in.Type)
}

params := kernel.ProxyNewParams{
Type: proxyType,
}

if in.Name != "" {
params.Name = kernel.Opt(in.Name)
}

// Build config based on type
switch proxyType {
case kernel.ProxyNewParamsTypeDatacenter:
if in.Country == "" {
return fmt.Errorf("--country is required for datacenter proxy type")
}
params.Config = kernel.ProxyNewParamsConfigUnion{
OfProxyNewsConfigDatacenterProxyConfig: &kernel.ProxyNewParamsConfigDatacenterProxyConfig{
Country: in.Country,
},
}

case kernel.ProxyNewParamsTypeIsp:
if in.Country == "" {
return fmt.Errorf("--country is required for ISP proxy type")
}
params.Config = kernel.ProxyNewParamsConfigUnion{
OfProxyNewsConfigIspProxyConfig: &kernel.ProxyNewParamsConfigIspProxyConfig{
Country: in.Country,
},
}

case kernel.ProxyNewParamsTypeResidential:
config := kernel.ProxyNewParamsConfigResidentialProxyConfig{}

// Validate that if city is provided, country must also be provided
if in.City != "" && in.Country == "" {
return fmt.Errorf("--country is required when --city is specified")
}

if in.Country != "" {
config.Country = kernel.Opt(in.Country)
}
if in.City != "" {
config.City = kernel.Opt(in.City)
}
if in.State != "" {
config.State = kernel.Opt(in.State)
}
if in.Zip != "" {
config.Zip = kernel.Opt(in.Zip)
}
if in.ASN != "" {
config.Asn = kernel.Opt(in.ASN)
}
if in.OS != "" {
// Validate OS value
switch in.OS {
case "windows", "macos", "android":
config.Os = in.OS
default:
return fmt.Errorf("invalid OS value: %s (must be windows, macos, or android)", in.OS)
}
}
params.Config = kernel.ProxyNewParamsConfigUnion{
OfProxyNewsConfigResidentialProxyConfig: &config,
}

case kernel.ProxyNewParamsTypeMobile:
config := kernel.ProxyNewParamsConfigMobileProxyConfig{}

// Validate that if city is provided, country must also be provided
if in.City != "" && in.Country == "" {
return fmt.Errorf("--country is required when --city is specified")
}

if in.Country != "" {
config.Country = kernel.Opt(in.Country)
}
if in.City != "" {
config.City = kernel.Opt(in.City)
}
if in.State != "" {
config.State = kernel.Opt(in.State)
}
if in.Zip != "" {
config.Zip = kernel.Opt(in.Zip)
}
if in.ASN != "" {
config.Asn = kernel.Opt(in.ASN)
}
if in.Carrier != "" {
// The API will validate the carrier value
config.Carrier = in.Carrier
}
params.Config = kernel.ProxyNewParamsConfigUnion{
OfProxyNewsConfigMobileProxyConfig: &config,
}

case kernel.ProxyNewParamsTypeCustom:
if in.Host == "" {
return fmt.Errorf("--host is required for custom proxy type")
}
if in.Port == 0 {
return fmt.Errorf("--port is required for custom proxy type")
}

config := kernel.ProxyNewParamsConfigCreateCustomProxyConfig{
Host: in.Host,
Port: int64(in.Port),
}
if in.Username != "" {
config.Username = kernel.Opt(in.Username)
}
if in.Password != "" {
config.Password = kernel.Opt(in.Password)
}
params.Config = kernel.ProxyNewParamsConfigUnion{
OfProxyNewsConfigCreateCustomProxyConfig: &config,
}
}

pterm.Info.Printf("Creating %s proxy...\n", proxyType)

proxy, err := p.proxies.New(ctx, params)
if err != nil {
return util.CleanedUpSdkError{Err: err}
}

pterm.Success.Printf("Successfully created proxy\n")

// Display created proxy details
rows := pterm.TableData{{"Property", "Value"}}
rows = append(rows, []string{"ID", proxy.ID})

name := proxy.Name
if name == "" {
name = "-"
}
rows = append(rows, []string{"Name", name})
rows = append(rows, []string{"Type", string(proxy.Type)})

PrintTableNoPad(rows, true)
return nil
}

func runProxiesCreate(cmd *cobra.Command, args []string) error {
client := util.GetKernelClient(cmd)

// Get all flag values
proxyType, _ := cmd.Flags().GetString("type")
name, _ := cmd.Flags().GetString("name")
country, _ := cmd.Flags().GetString("country")
city, _ := cmd.Flags().GetString("city")
state, _ := cmd.Flags().GetString("state")
zip, _ := cmd.Flags().GetString("zip")
asn, _ := cmd.Flags().GetString("asn")
os, _ := cmd.Flags().GetString("os")
carrier, _ := cmd.Flags().GetString("carrier")
host, _ := cmd.Flags().GetString("host")
port, _ := cmd.Flags().GetInt("port")
username, _ := cmd.Flags().GetString("username")
password, _ := cmd.Flags().GetString("password")

svc := client.Proxies
p := ProxyCmd{proxies: &svc}
return p.Create(cmd.Context(), ProxyCreateInput{
Name: name,
Type: proxyType,
Country: country,
City: city,
State: state,
Zip: zip,
ASN: asn,
OS: os,
Carrier: carrier,
Host: host,
Port: port,
Username: username,
Password: password,
})
}
Loading