diff --git a/internal/api/agent/models.go b/internal/api/agent/models.go new file mode 100644 index 0000000..bd09c76 --- /dev/null +++ b/internal/api/agent/models.go @@ -0,0 +1,33 @@ +package agentapi + +type VPCCreateRequest struct { + Name string `json:"name"` +} + +type VPC struct { + Name string `json:"name"` + State string `json:"state"` +} + +type SubnetCreateRequest struct { + Name string `json:"name"` + VPC string `json:"vpc"` + VxlanID int `json:"vxlan_id"` + LocalIP string `json:"local_ip"` + GatewayIP string `json:"gateway_ip"` + CIDR string `json:"cidr"` +} + +type Subnet struct { + Name string `json:"name"` + State string `json:"state"` + VPC string `json:"vpc"` + VxlanID int `json:"vxlan_id"` + LocalIP string `json:"local_ip"` + GatewayIP string `json:"gateway_ip"` + CIDR string `json:"cidr"` +} + +type ErrorResponse struct { + Error string `json:"error"` +} diff --git a/internal/api/agent/server.go b/internal/api/agent/server.go index aebe2c3..75833c9 100644 --- a/internal/api/agent/server.go +++ b/internal/api/agent/server.go @@ -22,5 +22,12 @@ func (s *Server) Start(address string) { mux.HandleFunc("/subnets", s.SubnetsHandler) mux.HandleFunc("/subnets/", s.SubnetByNameHandler) log.Printf("API server listening on %s", address) - log.Fatal(http.ListenAndServe(address, mux)) + log.Fatal(http.ListenAndServe(address, logMiddleware(mux))) +} + +func logMiddleware(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + log.Printf("%s %s %s", r.RemoteAddr, r.Method, r.URL.Path) + next.ServeHTTP(w, r) + }) } diff --git a/internal/api/agent/subnet.go b/internal/api/agent/subnet.go index 52b27d0..289667f 100644 --- a/internal/api/agent/subnet.go +++ b/internal/api/agent/subnet.go @@ -9,22 +9,33 @@ import ( func (s *Server) SubnetByNameHandler(w http.ResponseWriter, r *http.Request) { name := strings.TrimPrefix(r.URL.Path, "/subnets/") if name == "" { - http.NotFound(w, r) + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusNotFound) + json.NewEncoder(w).Encode(ErrorResponse{Error: "resource not found"}) return } w.Header().Set("Content-Type", "application/json") switch r.Method { case http.MethodGet: - w.WriteHeader(http.StatusOK) - json.NewEncoder(w).Encode(map[string]string{"name": name}) + s.getSubnet(w, r, name) case http.MethodDelete: - s.queue.Submit(func() { - deleteSubnet(name) - }) - w.WriteHeader(http.StatusAccepted) + s.deleteSubnet(w, r, name) default: - http.Error(w, "method not allowed", http.StatusMethodNotAllowed) + http.Error(w, `{"error":"method not allowed"}`, http.StatusMethodNotAllowed) } } -func deleteSubnet(name string) {} +func (s *Server) getSubnet(w http.ResponseWriter, r *http.Request, name string) { + w.WriteHeader(http.StatusOK) + json.NewEncoder(w).Encode(Subnet{Name: name, State: "created"}) +} + +func (s *Server) deleteSubnet(w http.ResponseWriter, r *http.Request, name string) { + s.queue.Submit(func() { + destroySubnet(name) + }) + w.WriteHeader(http.StatusAccepted) + json.NewEncoder(w).Encode(Subnet{Name: name, State: "deleting"}) +} + +func destroySubnet(name string) {} diff --git a/internal/api/agent/subnets.go b/internal/api/agent/subnets.go index b3d587b..5e0ebdc 100644 --- a/internal/api/agent/subnets.go +++ b/internal/api/agent/subnets.go @@ -9,16 +9,44 @@ func (s *Server) SubnetsHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") switch r.Method { case http.MethodGet: - w.WriteHeader(http.StatusOK) - json.NewEncoder(w).Encode([]interface{}{}) + s.listSubnets(w, r) case http.MethodPost: - s.queue.Submit(func() { - createSubnet() - }) - w.WriteHeader(http.StatusAccepted) + s.postSubnet(w, r) default: - http.Error(w, "method not allowed", http.StatusMethodNotAllowed) + http.Error(w, `{"error":"method not allowed"}`, http.StatusMethodNotAllowed) } } -func createSubnet() {} +func (s *Server) listSubnets(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + json.NewEncoder(w).Encode([]Subnet{}) +} + +func (s *Server) postSubnet(w http.ResponseWriter, r *http.Request) { + var req SubnetCreateRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + w.WriteHeader(http.StatusBadRequest) + json.NewEncoder(w).Encode(ErrorResponse{Error: "invalid request body"}) + return + } + if req.Name == "" || req.VPC == "" || req.LocalIP == "" || req.GatewayIP == "" || req.CIDR == "" { + w.WriteHeader(http.StatusBadRequest) + json.NewEncoder(w).Encode(ErrorResponse{Error: "name, vpc, local_ip, gateway_ip and cidr are required"}) + return + } + s.queue.Submit(func() { + createSubnet(req) + }) + w.WriteHeader(http.StatusAccepted) + json.NewEncoder(w).Encode(Subnet{ + Name: req.Name, + State: "creating", + VPC: req.VPC, + VxlanID: req.VxlanID, + LocalIP: req.LocalIP, + GatewayIP: req.GatewayIP, + CIDR: req.CIDR, + }) +} + +func createSubnet(req SubnetCreateRequest) {} diff --git a/internal/api/agent/vpc.go b/internal/api/agent/vpc.go index 73cecd9..f1d34c1 100644 --- a/internal/api/agent/vpc.go +++ b/internal/api/agent/vpc.go @@ -9,22 +9,33 @@ import ( func (s *Server) VpcByNameHandler(w http.ResponseWriter, r *http.Request) { name := strings.TrimPrefix(r.URL.Path, "/vpcs/") if name == "" { - http.NotFound(w, r) + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusNotFound) + json.NewEncoder(w).Encode(ErrorResponse{Error: "resource not found"}) return } w.Header().Set("Content-Type", "application/json") switch r.Method { case http.MethodGet: - w.WriteHeader(http.StatusOK) - json.NewEncoder(w).Encode(map[string]string{"name": name}) + s.getVpc(w, r, name) case http.MethodDelete: - s.queue.Submit(func() { - deleteVpc(name) - }) - w.WriteHeader(http.StatusAccepted) + s.deleteVpc(w, r, name) default: - http.Error(w, "method not allowed", http.StatusMethodNotAllowed) + http.Error(w, `{"error":"method not allowed"}`, http.StatusMethodNotAllowed) } } -func deleteVpc(name string) {} +func (s *Server) getVpc(w http.ResponseWriter, r *http.Request, name string) { + w.WriteHeader(http.StatusOK) + json.NewEncoder(w).Encode(VPC{Name: name, State: "created"}) +} + +func (s *Server) deleteVpc(w http.ResponseWriter, r *http.Request, name string) { + s.queue.Submit(func() { + destroyVpc(name) + }) + w.WriteHeader(http.StatusAccepted) + json.NewEncoder(w).Encode(VPC{Name: name, State: "deleting"}) +} + +func destroyVpc(name string) {} diff --git a/internal/api/agent/vpcs.go b/internal/api/agent/vpcs.go index 57386b6..aba4e7a 100644 --- a/internal/api/agent/vpcs.go +++ b/internal/api/agent/vpcs.go @@ -9,16 +9,36 @@ func (s *Server) VpcsHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") switch r.Method { case http.MethodGet: - w.WriteHeader(http.StatusOK) - json.NewEncoder(w).Encode([]interface{}{}) + s.listVpcs(w, r) case http.MethodPost: - s.queue.Submit(func() { - createVpc() - }) - w.WriteHeader(http.StatusAccepted) + s.postVpc(w, r) default: - http.Error(w, "method not allowed", http.StatusMethodNotAllowed) + http.Error(w, `{"error":"method not allowed"}`, http.StatusMethodNotAllowed) } } -func createVpc() {} +func (s *Server) listVpcs(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + json.NewEncoder(w).Encode([]VPC{}) +} + +func (s *Server) postVpc(w http.ResponseWriter, r *http.Request) { + var req VPCCreateRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + w.WriteHeader(http.StatusBadRequest) + json.NewEncoder(w).Encode(ErrorResponse{Error: "invalid request body"}) + return + } + if req.Name == "" { + w.WriteHeader(http.StatusBadRequest) + json.NewEncoder(w).Encode(ErrorResponse{Error: "name is required"}) + return + } + s.queue.Submit(func() { + createVpc(req.Name) + }) + w.WriteHeader(http.StatusAccepted) + json.NewEncoder(w).Encode(VPC{Name: req.Name, State: "creating"}) +} + +func createVpc(name string) {}