Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use OS default config dirs + minor fixes #357

Merged
merged 4 commits into from
Jan 10, 2024
Merged
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
18 changes: 9 additions & 9 deletions Dockerfile.dev
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
ARG GOLANG_IMAGE=golang:1.21.5-alpine3.19@sha256:55f716237933c85cee01748700755b4ac8736fb1ca974c9aed051691b68d6dc2
ARG GOLANGCI_LINT_IMAGE=golangci/golangci-lint:latest@sha256:fb70c9b2e6d0763141f057abcafde7f88d5e4bb3b5882d6b14bc79382f04481c
ARG PACKER_IMAGE=hashicorp/packer:1.10@sha256:a10638519af09f5ecad52b6eb4eab489377e4e89f30ea46832f1f401a234d783
ARG TERRAFORM_IMAGE=hashicorp/terraform:1.6@sha256:d593c353357a3db5a795c2ba0b998580cf12bad9125807bd877092c2e813279b
ARG UBUNTU_IMAGE=ubuntu:mantic@sha256:8d093e0651575a6437cc4a3d561f892a345d263aeac6156ef378fe6a4ccabd4c
ARG GOLANG_IMAGE=golang:1.21.5-alpine3.19@sha256:4db4aac30880b978cae5445dd4a706215249ad4f43d28bd7cdf7906e9be8dd6b
ARG GOLANGCI_LINT_IMAGE=golangci/golangci-lint:latest@sha256:e699df940be1810b08ba6ec050bfc34cc1931027283b5a7f607fb6a67b503876
ARG PACKER_IMAGE=hashicorp/packer:1.10@sha256:1deccbc7bca80cccfc50218e269f87db33476fda79de814372db608715d000c0
ARG TERRAFORM_IMAGE=hashicorp/terraform:1.6@sha256:9a42ea97ea25b363f4c65be25b9ca52b1e511ea5bf7d56050a506ad2daa7af9d
ARG UBUNTU_IMAGE=ubuntu:mantic@sha256:cbc171ba52575fec0601f01abf6fdec67f8ed227658cacbc10d778ac3b218307

FROM ${GOLANGCI_LINT_IMAGE}

Expand All @@ -27,15 +27,15 @@ FROM ${PACKER_IMAGE} as PACKER
FROM ${TERRAFORM_IMAGE} as TERRAFORM
FROM ${UBUNTU_IMAGE}

WORKDIR simulator
WORKDIR /simulator

COPY --from=PACKER /bin/packer /usr/local/bin/packer
COPY --from=TERRAFORM /bin/terraform /usr/local/bin/terraform

RUN apt update && \
apt install -y ca-certificates openssh-client ansible-core && \
rm -rf /var/lib/apt/lists/* && \
ansible-galaxy collection install kubernetes.core
apt install -y ca-certificates openssh-client ansible-core && \
rm -rf /var/lib/apt/lists/* && \
ansible-galaxy collection install kubernetes.core

COPY --from=BUILDER /simulator /usr/local/bin/simulator

Expand Down
10 changes: 5 additions & 5 deletions cmd/container/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"github.com/controlplaneio/simulator/v2/core/tools"
"github.com/controlplaneio/simulator/v2/internal/cli"
"github.com/controlplaneio/simulator/v2/internal/config"
"github.com/controlplaneio/simulator/v2/internal/logging"
)

func main() {
Expand All @@ -19,11 +18,9 @@ func main() {
adminBundleDir := filepath.Join(simulatorDir, "config", "admin")
packerDir := filepath.Join(simulatorDir, "packer")
terraformWorkspaceDir := filepath.Join(simulatorDir, "terraform/workspaces/simulator")
ansibleConfigPath := filepath.Join(adminBundleDir, "ansible.cfg")
ansiblePlaybookDir := filepath.Join(simulatorDir, "ansible/playbooks")

// configure slog
logging.Configure()

conf := config.Config{}
if err := conf.Read(); err != nil {
slog.Error("failed to read config", "error", err)
Expand All @@ -43,7 +40,10 @@ func main() {
scenarioManager := tools.AnsiblePlaybook{
WorkingDir: adminBundleDir,
PlaybookDir: ansiblePlaybookDir,
Output: os.Stdout,
// Ansible complains on Windows+WSL that the directory ansible configuration is world writable
// and hence ignore the configuration unless explicitly set using the ANSIBLE_CONFIG environment variable.
Env: []string{"ANSIBLE_CONFIG=" + ansibleConfigPath},
Output: os.Stdout,
}

withStateBucketFlag := cli.WithFlag("stateBucket", "", "the name of the S3 bucket to store Terraform state")
Expand Down
16 changes: 5 additions & 11 deletions cmd/simulator/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package main

import (
"context"
"errors"
"log/slog"
"os"
"path/filepath"
Expand All @@ -14,7 +13,6 @@ import (
"github.com/controlplaneio/simulator/v2/internal/cli"
"github.com/controlplaneio/simulator/v2/internal/config"
"github.com/controlplaneio/simulator/v2/internal/docker"
"github.com/controlplaneio/simulator/v2/internal/logging"
)

const (
Expand All @@ -32,8 +30,6 @@ var (
)

func main() {
logging.Configure()

conf := config.Config{}
if err := conf.Read(); err != nil {
slog.Error("failed to read config", "error", err)
Expand Down Expand Up @@ -114,7 +110,7 @@ func main() {
}

amiManager := aws.EC2{}
amiCreator := tools.PackerContainer{
amiBuilder := tools.PackerContainer{
Client: dockerClient,
Config: dockerConfig,
}
Expand All @@ -141,7 +137,7 @@ func main() {
cli.WithContainerPullCmd(conf, dockerClient),
),
cli.WithAMICmd(
cli.WithAmiBuildCmd(amiCreator),
cli.WithAmiBuildCmd(amiBuilder),
cli.WithAMIListCmd(amiManager),
cli.WithAMIDeleteCmd(amiManager),
),
Expand Down Expand Up @@ -177,11 +173,9 @@ func main() {

func mkDirsIfNotExist(dirs ...string) {
for _, dir := range dirs {
if _, err := os.Stat(dir); errors.Is(err, os.ErrNotExist) {
if err := os.Mkdir(dir, ownerReadWriteExecute); err != nil {
slog.Error("failed to bundle directory", "dir", dir, "error", err)
os.Exit(1)
}
if err := os.MkdirAll(dir, ownerReadWriteExecute); err != nil {
slog.Error("failed to bundle directory", "dir", dir, "error", err)
os.Exit(1)
}
}
}
4 changes: 2 additions & 2 deletions core/aws/buckets.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ type BucketManager interface {
func NewS3Client(ctx context.Context) (*S3Client, error) {
cfg, err := config.LoadDefaultConfig(ctx)
if err != nil {
return nil, errors.Join(errors.New("failed to load default config"), err)
return nil, fmt.Errorf("failed to load default config: %w", err)
}

return &S3Client{
Expand All @@ -35,7 +35,7 @@ type S3Client struct {
func (c S3Client) Create(ctx context.Context, name string) error {
region, ok := os.LookupEnv("AWS_REGION")
if !ok {
return errors.New("failed to create bucket, aws region not set")
return errors.New("failed to create bucket, AWS_REGION not set")
}

var bucketAlreadyOwnedByYou *types.BucketAlreadyOwnedByYou
Expand Down
23 changes: 10 additions & 13 deletions core/aws/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,21 @@ import (
"os"
)

var (
//nolint: gochecknoglobals
// envKeys is a list of environment variables that are used by the AWS SDK.
envKeys = []string{
"AWS_PROFILE",
"AWS_REGION",
"AWS_ACCESS_KEY_ID",
"AWS_SECRET_ACCESS_KEY",
"AWS_SESSION_TOKEN",
}
)
// envKeys is a list of environment variables that are used by the AWS SDK.
var envKeys = []string{ //nolint: gochecknoglobals
"AWS_PROFILE",
"AWS_REGION",
"AWS_ACCESS_KEY_ID",
"AWS_SECRET_ACCESS_KEY",
"AWS_SESSION_TOKEN",
}

func EnvVars() []string {
env := make([]string, 0)

for _, key := range envKeys {
value, ok := os.LookupEnv(key)
if ok && len(value) > 0 {
value := os.Getenv(key)
if len(value) > 0 {
env = append(env, fmt.Sprintf("%s=%s", key, value))
}
}
Expand Down
8 changes: 5 additions & 3 deletions core/tools/ansible.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,14 @@ type ScenarioManager interface {
type AnsiblePlaybook struct {
WorkingDir string
PlaybookDir string
Env []string
Output io.Writer
}

func (p AnsiblePlaybook) Install(ctx context.Context, id string) error {
playbook := fmt.Sprintf("%s.yaml", id)

if err := ansiblePlaybookCommand(p.WorkingDir, p.PlaybookDir, playbook).Run(ctx, p.Output); err != nil {
if err := ansiblePlaybookCommand(p.WorkingDir, p.PlaybookDir, p.Env, playbook).Run(ctx, p.Output); err != nil {
return fmt.Errorf("failed to execute Ansible Playbook: %w", err)
}

Expand All @@ -37,15 +38,15 @@ func (p AnsiblePlaybook) Install(ctx context.Context, id string) error {
func (p AnsiblePlaybook) Uninstall(ctx context.Context, id string) error {
playbook := fmt.Sprintf("%s.yaml", id)

if err := ansiblePlaybookCommand(p.WorkingDir, p.PlaybookDir, playbook, "state=absent").
if err := ansiblePlaybookCommand(p.WorkingDir, p.PlaybookDir, p.Env, playbook, "state=absent").
Run(ctx, p.Output); err != nil {
return fmt.Errorf("failed to run Ansible Playbook with state=absent: %w", err)
}

return nil
}

func ansiblePlaybookCommand(workingDir, playbookDir, playbook string, extraVars ...string) runner {
func ansiblePlaybookCommand(workingDir, playbookDir string, env []string, playbook string, extraVars ...string) runner {
args := []string{
fmt.Sprintf("%s/%s", playbookDir, playbook),
}
Expand All @@ -61,6 +62,7 @@ func ansiblePlaybookCommand(workingDir, playbookDir, playbook string, extraVars
Executable: AnsiblePlaybookExecutable,
WorkingDir: workingDir,
Arguments: args,
Env: env,
}
}

Expand Down
5 changes: 3 additions & 2 deletions core/tools/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package tools

import (
"context"
"errors"
"fmt"
"io"
"log/slog"
Expand All @@ -15,6 +14,7 @@ type Executable string
type runner struct {
Executable Executable
WorkingDir string
Env []string
Arguments []string
}

Expand All @@ -26,10 +26,11 @@ func (c runner) Run(ctx context.Context, output io.Writer) error {
cmd.Dir = c.WorkingDir
cmd.Stdout = output
cmd.Stderr = output
cmd.Env = c.Env

err := cmd.Run()
if err != nil {
return errors.Join(errors.New("failed to run runner"), err)
return fmt.Errorf("failed to run runner: %w", err)
}

return nil
Expand Down
13 changes: 6 additions & 7 deletions core/tools/terraform.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package tools

import (
"context"
"errors"
"fmt"
"io"

Expand Down Expand Up @@ -31,13 +30,13 @@ func (t Terraform) Create(ctx context.Context, stateBucket string, stateKey stri
backend := backendConfig(stateBucket, stateKey)

if err := terraformInitCommand(t.WorkingDir, backend).Run(ctx, t.Output); err != nil {
return errors.Join(errors.New("failed to initialise terraform"), err)
return fmt.Errorf("failed to initialise terraform: %w", err)
}

vars := terraformVars(name)

if err := terraformCommand(t.WorkingDir, TerraformApply, vars).Run(ctx, t.Output); err != nil {
return errors.Join(errors.New("failed to apply terraform"), err)
return fmt.Errorf("failed to apply terraform: %w", err)
}

return nil
Expand All @@ -47,13 +46,13 @@ func (t Terraform) Destroy(ctx context.Context, stateBucket string, stateKey str
backend := backendConfig(stateBucket, stateKey)

if err := terraformInitCommand(t.WorkingDir, backend).Run(ctx, t.Output); err != nil {
return errors.Join(errors.New("failed to initialise terraform"), err)
return fmt.Errorf("failed to initialise terraform: %w", err)
}

vars := terraformVars(name)

if err := terraformCommand(t.WorkingDir, TerraformDestroy, vars).Run(ctx, t.Output); err != nil {
return errors.Join(errors.New("failed to apply terraform"), err)
return fmt.Errorf("failed to destroy terraform: %w", err)
}

return nil
Expand Down Expand Up @@ -128,7 +127,7 @@ func (p TerraformContainer) Create(ctx context.Context, stateBucket string, stat
}

if err := p.Client.Run(ctx, config); err != nil {
return errors.Join(errors.New("failed to create infra"), err)
return fmt.Errorf("failed to create infra: %w", err)
}

return nil
Expand All @@ -148,7 +147,7 @@ func (p TerraformContainer) Destroy(ctx context.Context, stateBucket string, sta
}

if err := p.Client.Run(ctx, config); err != nil {
return errors.Join(errors.New("failed to destroy infra"), err)
return fmt.Errorf("failed to destroy infra: %w", err)
}

return nil
Expand Down
38 changes: 28 additions & 10 deletions docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,25 +22,35 @@ flowchart TD
scenario --> install
```

[//]: # (TODO document commands)
[//]: # "TODO document commands"

## Configuration

The Simulator ClI is configured using the `simulator config` command.

By default, this writes its configuration file to `$HOME/.simulator/config.yaml`. This can be changed to an
alternative directory by setting the environment variable `SIMULATOR_DIR` to define the directory to use.
By default, this writes its configuration file `config.yaml` to the following
directories according to the target operating system family:

| OS Family | Directory |
| --------- | ------------------------------------------------------------------------------------ |
| Linux | $XDG_CONFIG_HOME/.config/simulator - defaults to /home/$USER/.config/simulator |
| Windows | %LOCALAPPDATA%/simulator - default to C:\Users\$env:USERNAME\AppData\Local\simulator |
| MacOS | $HOME/Library/Preferences/io.controlplane.simulator |

This can be changed to an alternative directory by setting the environment variable
`SIMULATOR_DIR` to define the directory to use.

The following flags can be used to configure the Simulator CLI.

| Name | Description |
|----------|-----------------------------------------------------------------------------------------------------------------|
| name | Used as the name, or name prefix for the Terraform created resources. Defaults to simulator. |
| bucket | The name of the S3 bucket to store Terraform state. Can be an existing bucket that you own. MUST be configured. |
| dev | Used to set the Simulator CLI into developer mode when working on new scenarios. |
| rootless | Used when running rootless Docker, to allow local directories to be written to from the container. |
| Name | Description |
| --------- | --------------------------------------------------------------------------------------------------------------- |
| name | Used as the name, or name prefix for the Terraform created resources. Defaults to simulator. |
| bucket | The name of the S3 bucket to store Terraform state. Can be an existing bucket that you own. MUST be configured. |
| dev | Used to set the Simulator CLI into developer mode when working on new scenarios. |
| rootless | Used when running rootless Docker, to allow local directories to be written to from the container. |
| print-dir | Print configuration directory |

[//]: # (TODO: document scenario development and link)
[//]: # "TODO: document scenario development and link"

A minimal configuration file will look like the following.

Expand All @@ -50,3 +60,11 @@ bucket: <your-s3-bucket>
container:
image: controlplane/simulator:latest
```

## Log level

The default log level is set to `error` by default.

It can be changed using the `--log-level` flag or by setting
`SIMULATOR_LOG_LEVEL` environment variable to one of
error, warn, info or debug.
Loading
Loading