Skip to content
Open
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
2 changes: 1 addition & 1 deletion .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
strategy:
matrix:
go-version:
- "1.21.x"
- "1.23.x"
os:
- "ubuntu-latest"
steps:
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ deco.json
decofile-prod.json
docker-compose.yml
# Fresh
tmp/runner-build
tmp/runner-build
./docs/*
109 changes: 109 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
.PHONY: all build run test clean lint dev docker-build docker-run help

# Go related variables
BINARY_NAME=ec2apigo
MAIN_FILE=main.go
BUILD_DIR=./tmp

# Docker related variables
DOCKER_IMAGE=ec2apigo
DOCKER_TAG=latest

# Go build flags
LDFLAGS=-ldflags "-w -s"

# Default target
all: build

# Build the application
build:
@echo "Building $(BINARY_NAME)..."
@go build -o $(BUILD_DIR)/$(BINARY_NAME) $(LDFLAGS) $(MAIN_FILE)

# Run the application
run:
@go run $(MAIN_FILE)

# Run with live reload using air
dev:
@echo "Starting development server with air..."
@air

# Run tests
test:
@echo "Running tests..."
@go test -v ./...

# Run tests with coverage
test-coverage:
@echo "Running tests with coverage..."
@go test -v -cover ./...

# Clean build artifacts
clean:
@echo "Cleaning..."
@rm -rf $(BUILD_DIR)
@go clean
@echo "Cleaned build cache"

# Run go fmt
fmt:
@echo "Running go fmt..."
@go fmt ./...

# Run go vet
vet:
@echo "Running go vet..."
@go vet ./...

# Install dependencies
deps:
@echo "Installing dependencies..."
@go mod download
@go mod tidy

# Run linter
lint:
@echo "Running linter..."
@if command -v golangci-lint >/dev/null; then \
golangci-lint run; \
else \
echo "golangci-lint is not installed. Installing..."; \
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest; \
golangci-lint run; \
fi

# Build Docker image
docker-build:
@echo "Building Docker image..."
@docker build -t $(DOCKER_IMAGE):$(DOCKER_TAG) -f docker/Dockerfile .

# Run Docker container
docker-run:
@echo "Running Docker container..."
@docker run -p 8080:8080 $(DOCKER_IMAGE):$(DOCKER_TAG)

# Install development tools
tools:
@echo "Installing development tools..."
@go install github.com/air-verse/air@latest
@go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest

# Display help information
help:
@echo "Available targets:"
@echo " all - Build the application (default)"
@echo " build - Build the application"
@echo " run - Run the application"
@echo " dev - Run the application with live reload using air"
@echo " test - Run tests"
@echo " test-coverage - Run tests with coverage"
@echo " clean - Clean build artifacts"
@echo " fmt - Run go fmt"
@echo " vet - Run go vet"
@echo " deps - Install dependencies"
@echo " lint - Run linter"
@echo " tools - Install development tools"
@echo " docker-build - Build Docker image"
@echo " docker-run - Run Docker container"
@echo " help - Display this help message"
2 changes: 1 addition & 1 deletion api/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import (
log "github.com/sirupsen/logrus"
)

// PingHandler responds to ping requests
// PingHandler Ping Returns a response of pong if the application is up and running
func (s *server) PingHandler(w http.ResponseWriter, r *http.Request) {
w = LogWriter{w}
log.Debug("Ping/Pong")
Expand Down
180 changes: 180 additions & 0 deletions api/handlers_resourcegroups.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
/*
Copyright © 2021 Yale University

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package api

import (
"encoding/json"
"github.com/gorilla/mux"
log "github.com/sirupsen/logrus"
"net/http"

"github.com/YaleSpinup/apierror"
rg2 "github.com/YaleSpinup/ec2-api/resourcegroups"
"github.com/aws/aws-sdk-go/service/resourcegroups"
)

// ResourceGroupsCreateHandler handles the creation of a new resource group
func (s *server) ResourceGroupsCreateHandler(w http.ResponseWriter, r *http.Request) {
w = LogWriter{w}
w.Header().Set("Access-Control-Allow-Origin", "*")

vars := mux.Vars(r)
account := vars["account"]

log.Debugf("Creating resource group in account: %s", account)

// Initialize the resource groups service for this account
if err := s.setResourceGroupsService(account); err != nil {
log.Errorf("Failed to initialize resource groups service: %v", err)
handleError(w, apierror.New(apierror.ErrBadRequest, "failed to initialize resource groups service", err))
return
}

// Updated input struct to match AWS SDK expectations
var input struct {
Name string `json:"name"`
Description string `json:"description"`
Query struct {
Type string `json:"Type"`
Query string `json:"Query"`
} `json:"query"`
}

if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
handleError(w, apierror.New(apierror.ErrBadRequest, "invalid json input", err))
return
}

if input.Name == "" {
handleError(w, apierror.New(apierror.ErrBadRequest, "name is required", nil))
return
}

// Convert the input to AWS SDK format
createInput := rg2.CreateGroupInput{
Name: input.Name,
Description: input.Description,
ResourceQuery: &resourcegroups.ResourceQuery{
Type: &input.Query.Type,
Query: &input.Query.Query,
},
}

group, err := s.resourceGroups.CreateGroup(createInput)
if err != nil {
handleError(w, apierror.New(apierror.ErrBadRequest, "failed to create resource group", err))
return
}

handleResponseOk(w, group)
}

// ResourceGroupsListHandler lists all resource groups
func (s *server) ResourceGroupsListHandler(w http.ResponseWriter, r *http.Request) {
w = LogWriter{w}
w.Header().Set("Access-Control-Allow-Origin", "*")

vars := mux.Vars(r)
account := vars["account"]

log.Debugf("Listing resource groups in account: %s", account)

if err := s.setResourceGroupsService(account); err != nil {
log.Errorf("Failed to initialize resource groups service: %v", err)
handleError(w, apierror.New(apierror.ErrBadRequest, "failed to initialize resource groups service", err))
return
}

groups, err := s.resourceGroups.ListGroups()
if err != nil {
handleError(w, apierror.New(apierror.ErrBadRequest, "failed to list resource groups", err))
return
}

log.Infof("Successfully listed resource groups for account: %s", account)
handleResponseOk(w, groups)
}

// ResourceGroupsGetHandler gets details of a specific resource group and its resources
func (s *server) ResourceGroupsGetHandler(w http.ResponseWriter, r *http.Request) {
w = LogWriter{w}
w.Header().Set("Access-Control-Allow-Origin", "*")

vars := mux.Vars(r)
account := vars["account"]
groupName := vars["id"]

log.Debugf("Getting resource group %s in account: %s", groupName, account)

if err := s.setResourceGroupsService(account); err != nil {
log.Errorf("Failed to initialize resource groups service: %v", err)
handleError(w, apierror.New(apierror.ErrBadRequest, "failed to initialize resource groups service", err))
return
}

// Get group details
group, err := s.resourceGroups.GetGroup(groupName)
if err != nil {
handleError(w, apierror.New(apierror.ErrBadRequest, "failed to get resource group", err))
return
}

// Get resources in the group
resources, err := s.resourceGroups.ListGroupResources(groupName)
if err != nil {
handleError(w, apierror.New(apierror.ErrBadRequest, "failed to list group resources", err))
return
}

// Combine group details and resources
response := struct {
Group *resourcegroups.Group `json:"Group"`
Resources interface{} `json:"Resources"`
}{
Group: group,
Resources: resources,
}

log.Infof("Successfully retrieved resource group: %s", groupName)
handleResponseOk(w, response)
}

// ResourceGroupsDeleteHandler handles deletion of a resource group
func (s *server) ResourceGroupsDeleteHandler(w http.ResponseWriter, r *http.Request) {
w = LogWriter{w}
w.Header().Set("Access-Control-Allow-Origin", "*")

vars := mux.Vars(r)
account := vars["account"]
groupName := vars["id"]

log.Debugf("Deleting resource group %s in account: %s", groupName, account)

if err := s.setResourceGroupsService(account); err != nil {
log.Errorf("Failed to initialize resource groups service: %v", err)
handleError(w, apierror.New(apierror.ErrBadRequest, "failed to initialize resource groups service", err))
return
}

if err := s.resourceGroups.DeleteGroup(groupName); err != nil {
handleError(w, apierror.New(apierror.ErrBadRequest, "failed to delete resource group", err))
return
}

log.Infof("Successfully deleted resource group: %s", groupName)
handleResponseOk(w, nil)
}
Loading
Loading