diff --git a/cmd/ctrlc/ctrlc.go b/cmd/ctrlc/ctrlc.go index 0516587..b558b7a 100644 --- a/cmd/ctrlc/ctrlc.go +++ b/cmd/ctrlc/ctrlc.go @@ -18,6 +18,15 @@ var ( func init() { cobra.OnInitialize(initConfig) cmd.PersistentFlags().StringVar(&cfgFile, "config", "", "Config file (default is $HOME/.ctrlc.yaml)") + viper.BindEnv("config", "CTRLPLANE_CONFIG") + + cmd.PersistentFlags().String("url", "", "API URL") + viper.BindPFlag("url", cmd.PersistentFlags().Lookup("url")) + viper.BindEnv("url", "CTRLPLANE_URL") + + cmd.PersistentFlags().String("api-key", "", "API key for authentication") + viper.BindPFlag("api-key", cmd.PersistentFlags().Lookup("api-key")) + viper.BindEnv("api-key", "CTRLPLANE_API_KEY") } func main() { diff --git a/cmd/ctrlc/root/agent/run/run.go b/cmd/ctrlc/root/agent/run/run.go index d410611..726c7bd 100644 --- a/cmd/ctrlc/root/agent/run/run.go +++ b/cmd/ctrlc/root/agent/run/run.go @@ -2,7 +2,6 @@ package run import ( "log" - "os" "strings" "time" @@ -12,23 +11,20 @@ import ( ) func NewAgentRunCmd() *cobra.Command { - var proxyAddr string var agentName string var workspace string var metadata map[string]string var insecure bool - var targetId string + var associatedResources []string cmd := &cobra.Command{ Use: "run", Short: "Run the agent", Long: `Run the agent to establish connection with the proxy.`, RunE: func(cmd *cobra.Command, args []string) error { - if proxyAddr == "" { - proxyAddr = viper.GetString("url") - proxyAddr = strings.TrimPrefix(proxyAddr, "https://") - proxyAddr = strings.TrimPrefix(proxyAddr, "http://") - } + proxyAddr := viper.GetString("url") + proxyAddr = strings.TrimPrefix(proxyAddr, "https://") + proxyAddr = strings.TrimPrefix(proxyAddr, "http://") if insecure { proxyAddr = "ws://" + proxyAddr @@ -36,21 +32,14 @@ func NewAgentRunCmd() *cobra.Command { proxyAddr = "wss://" + proxyAddr } - apiKey := os.Getenv("CTRLPLANE_API_KEY") - if apiKey == "" { - apiKey = viper.GetString("api-key") - } - - opts := []func(*agent.Agent){ - agent.WithMetadata(metadata), - agent.WithHeader("X-API-Key", apiKey), - agent.WithHeader("X-Workspace", workspace), - } - + apiKey := viper.GetString("api-key") agent := agent.NewAgent( proxyAddr, agentName, - opts..., + agent.WithMetadata(metadata), + agent.WithHeader("X-API-Key", apiKey), + agent.WithHeader("X-Workspace", workspace), + agent.WithAssociatedResources(associatedResources), ) backoff := time.Second @@ -72,12 +61,11 @@ func NewAgentRunCmd() *cobra.Command { SilenceUsage: true, } - cmd.Flags().StringVarP(&proxyAddr, "proxy", "p", "app.ctrlplane.dev", "Proxy address to connect through") cmd.Flags().StringVarP(&agentName, "name", "n", "", "Name for this agent") cmd.Flags().StringVarP(&workspace, "workspace", "w", "", "Workspace for this agent") - cmd.Flags().StringVarP(&targetId, "target", "t", "", "Target ID to link this agent too") cmd.Flags().StringToStringVar(&metadata, "metadata", make(map[string]string), "Metadata key-value pairs (e.g. --metadata key=value)") - cmd.Flags().BoolVar(&insecure, "insecure", false, "Allow insecure connections") + cmd.Flags().BoolVar(&insecure, "insecure", false, "Allow insecure connections (a.k use ws://)") + cmd.Flags().StringArrayVarP(&associatedResources, "associated-resource", "r", []string{}, "Resource ID or Identifier to associate this agent with") cmd.MarkFlagRequired("workspace") cmd.MarkFlagRequired("name") diff --git a/cmd/ctrlc/root/api/api.go b/cmd/ctrlc/root/api/api.go index 29b8a28..188830d 100644 --- a/cmd/ctrlc/root/api/api.go +++ b/cmd/ctrlc/root/api/api.go @@ -31,14 +31,9 @@ func NewAPICmd() *cobra.Command { return cmd.Help() }, } - cmd.PersistentFlags().String("url", "", "API URL") - cmd.PersistentFlags().String("api-key", "", "API key for authentication") cmd.PersistentFlags().String("template", "", "Template for output format. Accepts Go template format (e.g. --template='{{.status.phase}}')") cmd.PersistentFlags().String("format", "json", "Output format. Accepts 'json' or 'yaml'") - viper.BindPFlag("url", cmd.PersistentFlags().Lookup("url")) - viper.BindPFlag("api-key", cmd.PersistentFlags().Lookup("api-key")) - cmd.AddCommand(get.NewGetCmd()) cmd.AddCommand(create.NewCreateCmd()) cmd.AddCommand(upsert.NewUpsertCmd()) diff --git a/cmd/ctrlc/root/api/delete/delete.go b/cmd/ctrlc/root/api/delete/delete.go index 90406a9..467fb95 100644 --- a/cmd/ctrlc/root/api/delete/delete.go +++ b/cmd/ctrlc/root/api/delete/delete.go @@ -15,7 +15,6 @@ func NewDeleteCmd() *cobra.Command { }, } - cmd.AddCommand(resource.NewDeleteResourceCmd()) return cmd diff --git a/pkg/agent/agent.go b/pkg/agent/agent.go index 1953e0d..6361bdf 100644 --- a/pkg/agent/agent.go +++ b/pkg/agent/agent.go @@ -6,6 +6,8 @@ import ( "log" "net/http" "os" + "runtime" + "strconv" "time" "github.com/creack/pty" @@ -34,13 +36,14 @@ func GetControllerProxyURL() string { } type Agent struct { - headers http.Header - client *client.Client - serverURL string - agentName string - StopSignal chan struct{} - metadata map[string]string - manager *ptysession.Manager + headers http.Header + client *client.Client + serverURL string + agentName string + StopSignal chan struct{} + metadata map[string]string + associatedResources []string + manager *ptysession.Manager } func NewAgent(serverURL, agentName string, opts ...func(*Agent)) *Agent { @@ -48,12 +51,13 @@ func NewAgent(serverURL, agentName string, opts ...func(*Agent)) *Agent { headers.Set("User-Agent", "ctrlplane-cli") headers.Set("X-Agent-Name", agentName) agent := &Agent{ - headers: headers, - serverURL: serverURL, - agentName: agentName, - StopSignal: make(chan struct{}), - metadata: make(map[string]string), - manager: ptysession.GetManager(), + headers: headers, + serverURL: serverURL, + agentName: agentName, + StopSignal: make(chan struct{}), + metadata: make(map[string]string), + manager: ptysession.GetManager(), + associatedResources: []string{}, } for _, opt := range opts { opt(agent) @@ -135,12 +139,23 @@ func (a *Agent) handleConnect() { log.Printf("Agent %s connected to server", a.agentName) connectPayload := payloads.AgentConnectJson{ - Type: payloads.AgentConnectJsonTypeAgentConnect, - Name: a.agentName, - Config: map[string]interface{}{}, - Metadata: a.metadata, + Type: payloads.AgentConnectJsonTypeAgentConnect, + Name: a.agentName, + Config: map[string]interface{}{}, + Metadata: a.metadata, + AssociatedResources: a.associatedResources, } + var memStats runtime.MemStats + runtime.ReadMemStats(&memStats) + connectPayload.Metadata["go/memstats/totalalloc"] = strconv.FormatUint(memStats.TotalAlloc, 10) + connectPayload.Metadata["go/memstats/sys"] = strconv.FormatUint(memStats.Sys, 10) + connectPayload.Metadata["runtime/os"] = runtime.GOOS + connectPayload.Metadata["runtime/arch"] = runtime.GOARCH + connectPayload.Metadata["go/version"] = runtime.Version() + connectPayload.Metadata["go/compiler"] = runtime.Compiler + connectPayload.Metadata["go/numcpu"] = strconv.Itoa(runtime.NumCPU()) + data, err := json.Marshal(connectPayload) if err != nil { log.Printf("Error marshaling connect payload: %v", err) diff --git a/pkg/agent/options.go b/pkg/agent/options.go index beee3a1..ec2681a 100644 --- a/pkg/agent/options.go +++ b/pkg/agent/options.go @@ -11,3 +11,10 @@ func WithHeader(key string, value string) func(*Agent) { a.headers.Set(key, value) } } + + +func WithAssociatedResources(resources []string) func(*Agent) { + return func(a *Agent) { + a.associatedResources = resources + } +} diff --git a/pkg/payloads/agent_connect_generated.go b/pkg/payloads/agent_connect_generated.go index a56fc58..119a9ff 100644 --- a/pkg/payloads/agent_connect_generated.go +++ b/pkg/payloads/agent_connect_generated.go @@ -18,6 +18,9 @@ type AgentConnectJson struct { // Type of payload - must be agent.register Type AgentConnectJsonType `json:"type" yaml:"type" mapstructure:"type"` + + // Optional list of resource IDs or identifiers to associate this agent with + AssociatedResources []string `json:"associatedResources,omitempty" yaml:"associatedResources,omitempty" mapstructure:"associatedResources,omitempty"` } // Optional configuration for the agent