From 0dfb38fcaa71610dfaccdd63fe2948965e40df74 Mon Sep 17 00:00:00 2001 From: anthdm Date: Wed, 3 Jan 2024 06:54:38 +0100 Subject: [PATCH] cli and api list endpoints --- cmd/cli/main.go | 31 +++++++++++++++++++++++++----- pkg/api/server.go | 9 +++++++++ pkg/client/client.go | 22 +++++++++++++++++++++ pkg/storage/redis.go | 43 +++++++++++++++++++++++++++++++++++++++--- pkg/storage/storage.go | 1 + 5 files changed, 98 insertions(+), 8 deletions(-) diff --git a/cmd/cli/main.go b/cmd/cli/main.go index 3a71615..b74db59 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -30,6 +30,7 @@ deploy Deploy an app to the cloud [deploy ] help Show usage `) + os.Exit(0) } type stringList []string @@ -64,7 +65,6 @@ func main() { args := flagset.Args() if len(args) == 0 { printUsage() - return } c := client.New(client.NewConfig().WithURL(config.GetApiUrl())) @@ -74,7 +74,17 @@ func main() { switch args[0] { case "endpoint": - command.handleCreateEndpoint(args[1:]) + if len(args) < 2 { + printUsage() + } + switch args[1] { + case "create": + command.handleCreateEndpoint(args) + case "list": + command.handleListEndpoints(args) + default: + printUsage() + } case "deploy": command.handleDeploy(args[1:]) case "help": @@ -88,13 +98,24 @@ type command struct { client *client.Client } +func (c command) handleListEndpoints(args []string) { + endpoints, err := c.client.ListEndpoints() + if err != nil { + printErrorAndExit(err) + } + b, err := json.MarshalIndent(endpoints, "", " ") + if err != nil { + printErrorAndExit(err) + } + fmt.Println(string(b)) +} + func (c command) handleCreateEndpoint(args []string) { - if len(args) != 1 { + if len(args) != 3 { printUsage() - return } params := api.CreateEndpointParams{ - Name: args[0], + Name: args[2], Environment: makeEnvMap(env), } endpoint, err := c.client.CreateEndpoint(params) diff --git a/pkg/api/server.go b/pkg/api/server.go index 4bb307b..aa344d2 100644 --- a/pkg/api/server.go +++ b/pkg/api/server.go @@ -40,6 +40,7 @@ func (s *Server) initRouter() { s.router = chi.NewRouter() s.router.Get("/status", handleStatus) s.router.Get("/endpoint/{id}", makeAPIHandler(s.handleGetEndpoint)) + s.router.Get("/endpoint", makeAPIHandler(s.handleGetEndpoints)) s.router.Get("/endpoint/{id}/metrics", makeAPIHandler(s.handleGetEndpointMetrics)) s.router.Post("/endpoint", makeAPIHandler(s.handleCreateEndpoint)) s.router.Post("/endpoint/{id}/deploy", makeAPIHandler(s.handleCreateDeploy)) @@ -135,6 +136,14 @@ func (s *Server) handleGetEndpoint(w http.ResponseWriter, r *http.Request) error return writeJSON(w, http.StatusOK, endpoint) } +func (s *Server) handleGetEndpoints(w http.ResponseWriter, r *http.Request) error { + endpoints, err := s.store.GetEndpoints() + if err != nil { + return writeJSON(w, http.StatusNotFound, ErrorResponse(err)) + } + return writeJSON(w, http.StatusOK, endpoints) +} + // CreateRollbackParams holds all the necessary fields to rollback your application // to a specific deploy id (version). type CreateRollbackParams struct { diff --git a/pkg/client/client.go b/pkg/client/client.go index 9cb001f..5dc8950 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -68,6 +68,9 @@ func (c *Client) CreateEndpoint(params api.CreateEndpointParams) (*types.Endpoin func (c *Client) CreateDeploy(endpointID uuid.UUID, blob io.Reader, params api.CreateDeployParams) (*types.Deploy, error) { url := fmt.Sprintf("%s/endpoint/%s/deploy", c.config.url, endpointID) req, err := http.NewRequest("POST", url, blob) + if err != nil { + return nil, err + } req.Header.Add("Content-Type", "application/octet-stream") resp, err := c.Do(req) if err != nil { @@ -82,3 +85,22 @@ func (c *Client) CreateDeploy(endpointID uuid.UUID, blob io.Reader, params api.C } return &deploy, nil } + +func (c *Client) ListEndpoints() ([]types.Endpoint, error) { + url := fmt.Sprintf("%s/endpoint", c.config.url) + req, err := http.NewRequest("GET", url, nil) + if err != nil { + return nil, err + } + req.Header.Add("Content-Type", "application/json") + resp, err := c.Do(req) + if err != nil { + return nil, err + } + var endpoints []types.Endpoint + if err := json.NewDecoder(resp.Body).Decode(&endpoints); err != nil { + return nil, err + } + + return endpoints, nil +} diff --git a/pkg/storage/redis.go b/pkg/storage/redis.go index 440db1d..1014b5f 100644 --- a/pkg/storage/redis.go +++ b/pkg/storage/redis.go @@ -10,10 +10,14 @@ import ( "github.com/vmihailenco/msgpack/v5" ) +// RedisStore is the Redis implementation of a Storage interface. type RedisStore struct { client *redis.Client } +// NewRedisStore returns a new RedisStore. +// +// This will return an error if we failed to ping the Redis server. func NewRedisStore() (*RedisStore, error) { client := redis.NewClient(&redis.Options{}) err := client.Ping(context.Background()).Err() @@ -31,12 +35,18 @@ func (s *RedisStore) CreateEndpoint(endpoint *types.Endpoint) error { if err != nil { return err } - return s.client.Set(context.Background(), endpoint.ID.String(), b, 0).Err() + key := makeKey("endpoint", endpoint.ID) + return s.client.Set(context.Background(), key, b, 0).Err() } func (s *RedisStore) GetEndpoint(id uuid.UUID) (*types.Endpoint, error) { + key := makeKey("endpoint", id) + return s.getEndpoint(key) +} + +func (s *RedisStore) getEndpoint(id string) (*types.Endpoint, error) { var endpoint types.Endpoint - b, err := s.client.Get(context.Background(), id.String()).Bytes() + b, err := s.client.Get(context.Background(), id).Bytes() if err != nil { return nil, err } @@ -44,6 +54,28 @@ func (s *RedisStore) GetEndpoint(id uuid.UUID) (*types.Endpoint, error) { return &endpoint, err } +func (s *RedisStore) GetEndpoints() ([]types.Endpoint, error) { + var ( + cursor uint64 + pattern = "endpoint_*" + ) + keys, cursor, err := s.client.Scan(context.Background(), cursor, pattern, 0).Result() + if err != nil { + return []types.Endpoint{}, err + } + + endpoints := make([]types.Endpoint, len(keys)) + for i, key := range keys { + endpoint, err := s.getEndpoint(key) + if err != nil { + return nil, err + } + endpoints[i] = *endpoint + } + + return endpoints, nil +} + func (s *RedisStore) UpdateEndpoint(id uuid.UUID, params UpdateEndpointParams) error { endpoint, err := s.GetEndpoint(id) if err != nil { @@ -64,7 +96,8 @@ func (s *RedisStore) UpdateEndpoint(id uuid.UUID, params UpdateEndpointParams) e if err != nil { return err } - return s.client.Set(context.Background(), endpoint.ID.String(), b, 0).Err() + key := makeKey("endpoint", endpoint.ID) + return s.client.Set(context.Background(), key, b, 0).Err() } func (s *RedisStore) GetDeploy(id uuid.UUID) (*types.Deploy, error) { @@ -102,3 +135,7 @@ func (s *RedisStore) GetRuntimeMetrics(id uuid.UUID) ([]types.RuntimeMetric, err err = msgpack.Unmarshal(b, &metrics) return metrics, err } + +func makeKey(prefix string, id uuid.UUID) string { + return fmt.Sprintf("%s_%s", prefix, id) +} diff --git a/pkg/storage/storage.go b/pkg/storage/storage.go index a860518..65c6306 100644 --- a/pkg/storage/storage.go +++ b/pkg/storage/storage.go @@ -9,6 +9,7 @@ type Store interface { CreateEndpoint(*types.Endpoint) error UpdateEndpoint(uuid.UUID, UpdateEndpointParams) error GetEndpoint(uuid.UUID) (*types.Endpoint, error) + GetEndpoints() ([]types.Endpoint, error) CreateDeploy(*types.Deploy) error GetDeploy(uuid.UUID) (*types.Deploy, error) }