-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
12 changed files
with
721 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
version: 2 | ||
updates: | ||
- package-ecosystem: gomod | ||
directory: "/" | ||
schedule: | ||
interval: weekly | ||
time: "20:00" | ||
open-pull-requests-limit: 10 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
name: Release | ||
on: | ||
push: | ||
branches: | ||
- "!**/*" | ||
tags: | ||
- "v*.*.*" | ||
|
||
jobs: | ||
release: | ||
name: Release | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Set up Go | ||
uses: actions/setup-go@v1 | ||
with: | ||
go-version: 1.17 | ||
|
||
- name: Check out code into the Go module directory | ||
uses: actions/checkout@v2 | ||
|
||
- name: Run GoReleaser | ||
uses: goreleaser/goreleaser-action@v1 | ||
with: | ||
version: latest | ||
args: release --rm-dist | ||
env: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
name: Test | ||
on: [push] | ||
jobs: | ||
test: | ||
strategy: | ||
matrix: | ||
go: | ||
- 1.17 | ||
name: Build | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Set up Go | ||
uses: actions/setup-go@v1 | ||
with: | ||
go-version: ${{ matrix.go }} | ||
id: go | ||
|
||
- name: Check out code into the Go module directory | ||
uses: actions/checkout@v2 | ||
|
||
- name: Build & Test | ||
run: | | ||
go test -race ./... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
# This is an example goreleaser.yaml file with some sane defaults. | ||
# Make sure to check the documentation at http://goreleaser.com | ||
before: | ||
hooks: | ||
- go mod download | ||
builds: | ||
- env: | ||
- CGO_ENABLED=0 | ||
main: ./cmd/flexentry | ||
binary: flexentry | ||
ldflags: | ||
- -s -w | ||
- -X main.Version=v{{.Version}} | ||
goos: | ||
- darwin | ||
- linux | ||
- windows | ||
goarch: | ||
- amd64 | ||
- arm64 | ||
release: | ||
prerelease: true | ||
archives: | ||
checksum: | ||
name_template: "checksums.txt" | ||
snapshot: | ||
name_template: "{{ .Env.NIGHTLY_VERSION }}" | ||
changelog: | ||
sort: asc | ||
filters: | ||
exclude: | ||
- "^docs:" | ||
- "^test:" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,81 @@ | ||
# flexentry | ||
![Latest GitHub release](https://img.shields.io/github/release/mashiike/flexentry.svg) | ||
![Github Actions test](https://github.com/mashiike/flexentry/workflows/Test/badge.svg?branch=main) | ||
[![Go Report Card](https://goreportcard.com/badge/mashiike/flexentry)](https://goreportcard.com/report/mashiike/flexentry) [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/mashiike/flexentry/blob/master/LICENSE) | ||
|
||
Flexible entry point for Amazon ECS Task and Amazon Lambda container images | ||
|
||
## Usage | ||
|
||
```Dockerfile | ||
FROM golang:1.17-buster | ||
|
||
RUN apt-get update && \ | ||
apt-get install -y unzip && \ | ||
apt-get clean | ||
|
||
RUN curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" && \ | ||
unzip awscliv2.zip && \ | ||
./aws/install && \ | ||
rm -R aws && \ | ||
rm awscliv2.zip | ||
|
||
ARG FLEXENTRY_VERSION=0.0.0 | ||
RUN curl -L https://github.com/mashiike/flexentry/releases/download/v${FLEXENTRY_VERSION}/flexentry_v${FLEXENTRY_VERSION}_linux_amd64.tar.gz | tar zxvf - && \ | ||
install flexentry_v${FLEXENTRY_VERSION}_linux_amd64/flexentry /usr/local/bin/ | ||
|
||
ENTRYPOINT ["flexentry"] | ||
``` | ||
|
||
Basically, all you have to do is specify the entry point of the container image. | ||
|
||
### Run on ECS Task | ||
|
||
```json | ||
{ | ||
"containerDefinitions": [ | ||
{ | ||
"command": [ | ||
"aws --version" | ||
], | ||
"essential": true, | ||
"image": "<ecr image path>", | ||
"name": "sample-app" | ||
} | ||
], | ||
"cpu": "256", | ||
"executionRoleArn": "arn:aws:iam::012345678910:role/ecsTaskExecutionRole", | ||
"family": "sample-task-definition", | ||
"memory": "512", | ||
"networkMode": "awsvpc", | ||
"requiresCompatibilities": [ | ||
"FARGATE" | ||
] | ||
} | ||
``` | ||
|
||
Decide what to execute with `command`, as in the task definition above. | ||
|
||
### Run on Lambda with container image | ||
|
||
If the environment variable `FLEXENTRY_COMMAND` is specified, the command will be executed. | ||
Otherwise, the command to be executed will be determined according to the payload of the event. | ||
|
||
```json | ||
{ | ||
"cmd": "aws --version", | ||
"description": "this is sample" | ||
} | ||
``` | ||
|
||
If the event payload is a string, it will be interpreted as a command. | ||
Otherwise, by default, it looks at the `cmd` key to decide which command to execute. | ||
To change the key, specify a jq expression for reference with `FLEXENTRY_COMMAND_JQ_EXPR`. | ||
For example `export FLEXENTRY_COMMAND_JQ_EXPR=".command"` | ||
|
||
When executed by Amazon Lambda, the event payload is passed directly to the standard input as JSON data. | ||
|
||
## LICENSE | ||
|
||
MIT | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package main | ||
|
||
import ( | ||
"context" | ||
"log" | ||
"os" | ||
"strings" | ||
"time" | ||
|
||
"github.com/fatih/color" | ||
"github.com/fujiwara/logutils" | ||
"github.com/mashiike/flexentry" | ||
) | ||
|
||
var ( | ||
Version string = "current" | ||
) | ||
|
||
func main() { | ||
logLevel := "info" | ||
if l := os.Getenv("FLEXENTRY_LOG_LEVEL"); l != "" { | ||
logLevel = l | ||
} | ||
filter := &logutils.LevelFilter{ | ||
Levels: []logutils.LogLevel{"debug", "info", "warn", "error"}, | ||
ModifierFuncs: []logutils.ModifierFunc{ | ||
logutils.Color(color.FgHiBlack), | ||
nil, | ||
logutils.Color(color.FgYellow), | ||
logutils.Color(color.FgRed, color.BgBlack), | ||
}, | ||
MinLevel: logutils.LogLevel(strings.ToLower(logLevel)), | ||
Writer: os.Stderr, | ||
} | ||
log.SetOutput(filter) | ||
log.Println("[debug] flexentry version:", Version) | ||
entrypoint := flexentry.Entrypoint{ | ||
Executer: flexentry.NewSSMWrapExecuter( | ||
flexentry.NewShellExecuter(), | ||
time.Minute, | ||
), | ||
} | ||
if err := entrypoint.Run(context.Background()); err != nil { | ||
log.Fatalln("[error] ", err) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
package flexentry | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"io" | ||
"log" | ||
"os" | ||
"os/exec" | ||
"strings" | ||
"sync" | ||
"time" | ||
|
||
"github.com/handlename/ssmwrap" | ||
) | ||
|
||
type Executer interface { | ||
Execute(ctx context.Context, stdin io.Reader, commands ...string) error | ||
} | ||
|
||
type ShellExecuter struct { | ||
shell string | ||
shellArgs []string | ||
|
||
stdout io.Writer | ||
stderr io.Writer | ||
} | ||
|
||
func NewShellExecuter() *ShellExecuter { | ||
return &ShellExecuter{ | ||
shell: "sh", | ||
shellArgs: []string{"-c"}, | ||
stdout: os.Stdout, | ||
stderr: os.Stderr, | ||
} | ||
} | ||
|
||
func (e *ShellExecuter) Execute(ctx context.Context, stdin io.Reader, commands ...string) error { | ||
args := make([]string, 0, len(e.shellArgs)+len(commands)) | ||
args = append(args, e.shellArgs...) | ||
args = append(args, strings.Join(commands, " ")) | ||
|
||
log.Printf("[debug] $%s %s", e.shell, strings.Join(args, " ")) | ||
cmd := exec.CommandContext(ctx, e.shell, args...) | ||
cmd.Env = os.Environ() | ||
p, _ := cmd.StdinPipe() | ||
go func() { | ||
defer p.Close() | ||
if stdin == nil { | ||
return | ||
} | ||
if _, err := io.Copy(p, stdin); err != nil { | ||
log.Println("[warn] failed to write stdinPipe:", err) | ||
} | ||
}() | ||
cmd.Stderr = e.stderr | ||
cmd.Stdout = e.stdout | ||
if err := cmd.Run(); err != nil { | ||
return err | ||
} | ||
return nil | ||
} | ||
|
||
func (e *ShellExecuter) Clone() *ShellExecuter { | ||
cloned := *e | ||
return &cloned | ||
} | ||
|
||
func (e *ShellExecuter) SetShell(shell string, shellArgs []string) *ShellExecuter { | ||
cloned := e.Clone() | ||
cloned.shell = shell | ||
cloned.shellArgs = make([]string, len(shellArgs)) | ||
copy(cloned.shellArgs, shellArgs) | ||
return cloned | ||
} | ||
|
||
func (e *ShellExecuter) SetOutput(stdout, stderr io.Writer) *ShellExecuter { | ||
cloned := e.Clone() | ||
cloned.stdout = stdout | ||
cloned.stderr = stderr | ||
return cloned | ||
} | ||
|
||
type SSMWrapExecuter struct { | ||
Executer | ||
|
||
mu sync.Mutex | ||
lastExported time.Time | ||
ssmCacheExpires time.Duration | ||
} | ||
|
||
func NewSSMWrapExecuter(executer Executer, cacheExpires time.Duration) *SSMWrapExecuter { | ||
return &SSMWrapExecuter{ | ||
Executer: executer, | ||
ssmCacheExpires: cacheExpires, | ||
} | ||
} | ||
|
||
func (e *SSMWrapExecuter) exportEnvWithCache() error { | ||
e.mu.Lock() | ||
defer e.mu.Unlock() | ||
if e.lastExported.IsZero() || e.lastExported.Before(time.Now().Add(-1*e.ssmCacheExpires)) { | ||
defer func() { | ||
e.lastExported = time.Now() | ||
}() | ||
return e.exportEnv() | ||
} | ||
log.Printf("[debug] exportEnv skipped. last exported at %s", e.lastExported.Format(time.RFC3339)) | ||
return nil | ||
} | ||
|
||
func (e *SSMWrapExecuter) exportEnv() error { | ||
if paths := os.Getenv("SSMWRAP_PATHS"); paths == "" { | ||
return nil | ||
} else { | ||
if err := ssmwrap.Export(ssmwrap.ExportOptions{ | ||
Paths: strings.Split(paths, ","), | ||
Retries: 3, | ||
}); err != nil { | ||
return fmt.Errorf("failed to fetch values from SSM paths %s: %w", paths, err) | ||
} | ||
log.Printf("[debug] exportEnv from SSMWRAP_PATHS=%s", paths) | ||
} | ||
return nil | ||
} | ||
|
||
func (e *SSMWrapExecuter) Execute(ctx context.Context, stdin io.Reader, commands ...string) error { | ||
if err := e.exportEnvWithCache(); err != nil { | ||
return err | ||
} | ||
return e.Executer.Execute(ctx, stdin, commands...) | ||
} |
Oops, something went wrong.