Skip to content

Commit

Permalink
add upload s3 and configurable port
Browse files Browse the repository at this point in the history
  • Loading branch information
leneffets committed Nov 15, 2024
1 parent 5245797 commit 638d177
Show file tree
Hide file tree
Showing 2 changed files with 166 additions and 94 deletions.
19 changes: 18 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ The server exposes two main endpoints: `/ssm` and `/s3`.

- Fetch decrypted parameters from AWS SSM.
- Fetch and serve files from AWS S3.
- Upload files to AWS S3.
- Basic CI/CD pipeline using GitHub Actions for automatic builds and tests.

## Requirements
Expand Down Expand Up @@ -37,7 +38,8 @@ The server exposes two main endpoints: `/ssm` and `/s3`.
## Running the Server

To start the HTTP server locally on port 3000, run the following command:

# Port may be changed via Environment, default 3000
export PORT=3000
go run cmd/server/main.go

## Endpoints
Expand Down Expand Up @@ -71,6 +73,21 @@ Fetch a file from an S3 bucket.
curl "http://localhost:3000/s3?bucket=example-bucket&key=example-key"
```

### Fetch S3 File

Upload a file to an S3 bucket.

- **URL:** `/s3`
- **Method:** `POST`
- **Query Parameters:**
- `bucket`: Name of the S3 bucket.
- `key`: Key of the file in the S3 bucket.
- **Example:**

```sh
curl -X POST -F 'file=@/path/to/your/file' "http://localhost:3000/s3?bucket=example-bucket&key=example-key"
```

## Running Tests

To run the tests:
Expand Down
241 changes: 148 additions & 93 deletions cmd/server/main.go
Original file line number Diff line number Diff line change
@@ -1,106 +1,161 @@
package main

import (
"context"
"io"
"log"
"net/http"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/aws/aws-sdk-go/service/ssm"
"github.com/aws/aws-sdk-go/service/ssm/ssmiface"
"context"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/aws/aws-sdk-go/service/ssm"
"github.com/aws/aws-sdk-go/service/ssm/ssmiface"
)

// Funktion, um SSM-Parameter abzurufen
func GetParameter(ctx context.Context, svc ssmiface.SSMAPI, name *string) (*ssm.GetParameterOutput, error) {
results, err := svc.GetParameterWithContext(ctx, &ssm.GetParameterInput{
Name: name,
WithDecryption: aws.Bool(true),
})
results, err := svc.GetParameterWithContext(ctx, &ssm.GetParameterInput{
Name: name,
WithDecryption: aws.Bool(true),
})
return results, err
}

return results, err
// Funktion, um ein Objekt aus S3 zu holen
func GetFromS3(ctx context.Context, sess *session.Session, bucket, key string) (io.ReadCloser, error) {
svc := s3.New(sess)
output, err := svc.GetObjectWithContext(ctx, &s3.GetObjectInput{
Bucket: aws.String(bucket),
Key: aws.String(key),
})
if err != nil {
return nil, err
}
return output.Body, nil
}

func GetFromS3(sess *session.Session, bucket, key string) (io.ReadCloser, error) {
svc := s3.New(sess)
output, err := svc.GetObject(&s3.GetObjectInput{
Bucket: aws.String(bucket),
Key: aws.String(key),
})
if err != nil {
return nil, err
}
return output.Body, nil
// Funktion, um ein Objekt in S3 zu legen
func PutToS3(ctx context.Context, sess *session.Session, bucket, key string, body io.ReadSeeker) error {
svc := s3.New(sess)
_, err := svc.PutObjectWithContext(ctx, &s3.PutObjectInput{
Bucket: aws.String(bucket),
Key: aws.String(key),
Body: body,
})
return err
}

func main() {
sess := session.Must(session.NewSessionWithOptions(session.Options{
SharedConfigState: session.SharedConfigEnable,
}))

svc := ssm.New(sess)

creds, err := sess.Config.Credentials.Get()
if err != nil {
log.Fatalf("Failed to get credentials: %v", err)
}
log.Printf("Using credentials: %s/%s\n", creds.AccessKeyID, creds.SecretAccessKey)


http.HandleFunc("/ssm", func(w http.ResponseWriter, r *http.Request) {
// Validate input parameter
id := r.URL.Query().Get("name")
if id == "" {
http.Error(w, "Parameter 'name' is required", http.StatusBadRequest)
return
}

// Set context with timeout
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

// Fetch parameter
results, err := GetParameter(ctx, svc, &id)
if err != nil {
log.Printf("Error fetching parameter: %v", err)
http.Error(w, "Error fetching parameter", http.StatusInternalServerError)
return
}

w.Header().Set("Content-Type", "text/plain")
w.Write([]byte(*results.Parameter.Value))
})

http.HandleFunc("/s3", func(w http.ResponseWriter, r *http.Request) {
// Validate input parameters
bucket := r.URL.Query().Get("bucket")
key := r.URL.Query().Get("key")
if bucket == "" || key == "" {
http.Error(w, "Parameters 'bucket' and 'key' are required", http.StatusBadRequest)
return
}

// Fetch file from S3
body, err := GetFromS3(sess, bucket, key)
if err != nil {
http.Error(w, "Error fetching file from S3", http.StatusInternalServerError)
log.Printf("Error fetching file from S3: %v", err)
return
}
defer body.Close()

// Send file content to client
w.Header().Set("Content-Type", "application/octet-stream")
if _, err := io.Copy(w, body); err != nil {
http.Error(w, "Error sending file", http.StatusInternalServerError)
log.Printf("Error sending file: %v", err)
return
}
})

log.Println("Server running on port 3000")
if err := http.ListenAndServe(":3000", nil); err != nil {
log.Fatalf("Error starting server: %v", err)
}
sess := session.Must(session.NewSessionWithOptions(session.Options{
SharedConfigState: session.SharedConfigEnable,
}))

svc := ssm.New(sess)

creds, err := sess.Config.Credentials.Get()
if err != nil {
log.Fatalf("Failed to get credentials: %v", err)
}
log.Printf("Using credentials: %s/%s\n", creds.AccessKeyID, creds.SecretAccessKey)

http.HandleFunc("/ssm", func(w http.ResponseWriter, r *http.Request) {
// Validate input parameter
id := r.URL.Query().Get("name")
if id == "" {
http.Error(w, "Parameter 'name' is required", http.StatusBadRequest)
return
}

// Set context with timeout
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

// Fetch parameter
results, err := GetParameter(ctx, svc, &id)
if err != nil {
log.Printf("Error fetching parameter: %v", err)
http.Error(w, "Error fetching parameter", http.StatusInternalServerError)
return
}

w.Header().Set("Content-Type", "text/plain")
w.Write([]byte(*results.Parameter.Value))
})

http.HandleFunc("/s3", func(w http.ResponseWriter, r *http.Request) {
bucket := r.URL.Query().Get("bucket")
key := r.URL.Query().Get("key")
if bucket == "" || key == "" {
http.Error(w, "Parameters 'bucket' and 'key' are required", http.StatusBadRequest)
return
}

ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

if r.Method == http.MethodGet {
body, err := GetFromS3(ctx, sess, bucket, key)
if err != nil {
http.Error(w, "Error fetching file from S3", http.StatusInternalServerError)
log.Printf("Error fetching file from S3: %v", err)
return
}
defer body.Close()

w.Header().Set("Content-Type", "application/octet-stream")
if _, err := io.Copy(w, body); err != nil {
http.Error(w, "Error sending file", http.StatusInternalServerError)
log.Printf("Error sending file: %v", err)
return
}
} else if r.Method == http.MethodPost {
file, _, err := r.FormFile("file")
if err != nil {
http.Error(w, "Error reading uploaded file", http.StatusBadRequest)
log.Printf("Error reading uploaded file: %v", err)
return
}
defer file.Close()

tempFile, err := ioutil.TempFile("", "upload-*.tmp")
if err != nil {
http.Error(w, "Error creating temporary file", http.StatusInternalServerError)
log.Printf("Error creating temporary file: %v", err)
return
}
defer os.Remove(tempFile.Name())

if _, err := io.Copy(tempFile, file); err != nil {
http.Error(w, "Error saving file", http.StatusInternalServerError)
log.Printf("Error saving file: %v", err)
return
}

tempFile.Seek(0, 0)

if err := PutToS3(ctx, sess, bucket, key, tempFile); err != nil {
http.Error(w, "Error uploading file to S3", http.StatusInternalServerError)
log.Printf("Error uploading file to S3: %v", err)
return
}

w.WriteHeader(http.StatusOK)
log.Printf(w, "File uploaded successfully")

Check failure on line 146 in cmd/server/main.go

View workflow job for this annotation

GitHub Actions / build

cannot use w (variable of type http.ResponseWriter) as string value in argument to log.Printf
} else {
http.Error(w, "Invalid method", http.StatusMethodNotAllowed)
}
})

port := os.Getenv("PORT")
if port == "" {
port = "3000"
}

log.Printf("Server running on port %s", port)
if err := http.ListenAndServe(":" + port, nil); err != nil {
log.Fatalf("Error starting server: %v", err)
}
}

0 comments on commit 638d177

Please sign in to comment.