ecschedule

package module
v0.17.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 2, 2025 License: MIT Imports: 40 Imported by: 0

README

ecschedule

Test Status MIT License PkgGoDev

ecschedule is a tool to manage ECS Scheduled Tasks.

Synopsis

% ecschedule [dump|apply|run|diff] -conf ecschedule.yaml -rule $ruleName

Description

The ecschedule manages ECS Schedule tasks using a configuration file (YAML, JSON or Jsonnet format) like following.

region: us-east-1
cluster: clusterName
rules:
trackingId: trackingId1
- name: taskName1
  description: task 1
  scheduleExpression: cron(30 15 ? * * *)
  taskDefinition: taskDefName
  containerOverrides:
  - name: containerName
    command: [subcommand1, arg]
    environment:
      HOGE: foo
      FUGA: {{ must_env `APP_FUGA` }}
- name: taskName2
  description: task2
  scheduleExpression: cron(30 16 ? * * *)
  taskDefinition: taskDefName2
  containerOverrides:
  - name: containerName2
    command: [subcommand2, arg]

Installation

% brew install Songmu/tap/ecschedule
# or
% go install github.com/Songmu/ecschedule/cmd/ecschedule@latest
GitHub Actions

Action Songmu/ecschedule@main installs ecschedule binary for Linux into /usr/local/bin. This action runs install only.

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: Songmu/ecschedule@main
      - run: |
          ecschedule -conf ecschedule.yaml apply -all
aqua

A declarative CLI Version Manager aqua can install ecschedule.

% aqua g -i Songmu/ecschedule

Quick Start

dump configuration YAML
% ecschedule dump --cluster clusterName --region us-east-1 > ecschedule.yaml

edit and adjust configuration file after it.

apply new or updated rule
% ecschedule -conf ecschedule.yaml apply -rule $ruleName

Before you apply it, you can check the diff in the following way.

% ecschedule -conf ecschedule.yaml diff -rule $ruleName
run rule

Execute run subcommand when want execute arbitrary timing.

% ecschedule -conf ecschedule.yaml run -rule $ruleName
Using the -prune option to manage rules

In version v0.9.1 and earlier, when rules were renamed or deleted from the configuration, the old rules remained and had to be deleted manually. With the -prune option introduced in v0.10.0, you can now automatically remove these old rules.

% ecschedule -conf ecschedule.yaml apply -all -prune

To see which rules would be deleted without actually removing them, you can use either diff or apply with -dry-run option:

% ecschedule -conf ecschedule.yaml diff -all -prune
% ecschedule -conf ecschedule.yaml apply -all -prune -dry-run

A trackingId is an optional key in a configuration file. If the trackingId is not explicitly specified, the cluster name will be used as the trackingId by default. When you explicitly specify the trackingId, it enables you to detect rule deletions for each file when executing multiple configuration files at different times.

Validation

The apply and run commands always perform validation (env, tfstate, ssm, task definition) before execution and cannot be disabled.

The diff command does not perform validation by default for quick review. To enable validation (recommended for CI/CD pipelines), use the -validate flag:

% ecschedule -conf ecschedule.yaml diff -all -prune -validate -u

This performs the same validation as apply and run, with a slight overhead, but ensures complete verification before applying changes.

Parallel Execution

The diff command supports parallel execution for improved performance with many rules:

% ecschedule -conf ecschedule.yaml diff -all -parallel 10
  • Default: parallel=1 (sequential, backward compatible)
  • Recommended: 1-10 (due to AWS API rate limits)
  • Note: Output order is not guaranteed when parallel > 1

Log Format

Unified diff format

ecschedule supports unified diff format (similar to git diff) with -u flag for both diff and apply commands:

% ecschedule -conf ecschedule.yaml diff -all -u
% ecschedule -conf ecschedule.yaml apply -all -dry-run -u

The diff command with -u flag outputs pure diff content without log prefixes or headers, making it suitable for piping to other tools. The apply command includes progress logs even with -u flag.

Color control

Colored output can be disabled with --no-color flag or environment variables:

% ecschedule -conf ecschedule.yaml diff -all -u --no-color
% NO_COLOR=1 ecschedule -conf ecschedule.yaml diff -all -u
% ECSCHEDULE_COLOR=false ecschedule -conf ecschedule.yaml apply -all -dry-run -u

Environment variables:

  • NO_COLOR: If set (value doesn't matter), disable colored output. Follows the NO_COLOR standard.
  • ECSCHEDULE_COLOR: Set to false or 0 to disable colored output (unified diff format only). Default is true.

Priority: --no-color flag > NO_COLOR > ECSCHEDULE_COLOR > default (colored).

Note: The --no-color flag is a subcommand-level flag and only affects unified diff format. The default pretty format always displays with colors.

Functions

You can use following functions in the configuration file.

  • env
    • expand environment variable or using default value
    • {{ env "ENV_NAME" "DEFAULT_VALUE" }}
  • must_env
    • expand environment variable
    • {{ must_env "ENV_NAME" }}

inspired by ecspresso.

Plugins

tfstate

tfstate plugin introduces a template function tfstate.

region: us-east-1
cluster: api
role: ecsEventsRole
rules:
- name: hoge-task-name
  description: hoge description
  scheduleExpression: cron(0 0 * * ? *)
  taskDefinition: task1
  group: xxx
  platform_version: 1.4.0
  launch_type: FARGATE
  network_configuration:
    aws_vpc_configuration:
      subnets:
      - {{ tfstate `aws_subnet.private-a.id` }}
      - {{ tfstate `aws_subnet.private-c.id` }}
      security_groups:
      - {{ tfstatef `data.aws_security_group.default['%s'].id` `first` }}
      - {{ tfstatef `data.aws_security_group.default['%s'].id` `second` }}
      assign_public_ip: ENABLED
  containerOverrides:
  - name: container1
    command: ["subcmd", "argument"]
    environment:
      HOGE_ENV: {{ env "DUMMY_HOGE_ENV" "HOGEGE" }}
    cpu: 1024
    memory: 1024
    memoryReservation: 512
  dead_letter_config:
    sqs: queue1
  propagateTags: TASK_DEFINITION
plugins:
- name: tfstate
  config:
    path: testdata/terraform.tfstate    # path to tfstate file
      # or url: s3://my-bucket/terraform.tfstate

{{ tfstate "resource_type.resource_name.attr" }} will expand to an attribute value of the resource in tfstate.

{{ tfstatef "resource_type.resource_name['%s'].attr" "index" }} is similar to {{ tfstatef "resource_type.resource_name['index'].attr" }}. This function is useful to build a resource address with environment variables.

{{ tfstatef `aws_subnet.ecs['%s'].id` (must_env `SERVICE`) }}
Multiple tfstate support

You can specify multiple tfstate files. Specify the func_prefix option to avoid conflicts between functions.

plugins:
- name: tfstate
  func_prefix: first_
  config:
    path: testdata/first_terraform.tfstate    # path to tfstate file
- name: tfstate
  func_prefix: second_
  config:
    path: testdata/second_terraform.tfstate    # path to tfstate file

In this case, the function must be called by the plugin function.

The plugin function takes the prefixed function name as the first argument and the function arguments as the second or later arguments.

{{ plugin `first_tfstate` `aws_subnet.private-a.id` }}
{{ plugin `second_tfstate` `aws_subnet.private-a.id` }}
ssm

ssm plugin introduces a template function ssm.

region: us-east-1
cluster: api
role: ecsEventsRole
rules:
- name: fuga-task-name
  description: fuga description
  scheduleExpression: cron(0 0 * * ? *)
  taskDefinition: task1
  group: xxx
  platform_version: 1.4.0
  launch_type: FARGATE
  network_configuration:
    aws_vpc_configuration:
      subnets:
      - {{ ssm `/subnet/private/a/id` }} # String type.
      - {{ ssm `/subnet/private/c/id` }}
      security_groups:
      - {{ ssm `security_group/default/id` 1 }} # StringList type.
      - {{ ssm `security_group/default/id` 2 }}
      assign_public_ip: ENABLED
  containerOverrides:
  - name: container1
    command: ["subcmd", "argument"]
    environment:
      FUGA_ENV: {{ ssm "/path/to/secretstring/fuga" }} # SecureString type.
    cpu: 1024
    memory: 1024
    memoryReservation: 512
  dead_letter_config:
    sqs: queue1
  propagateTags: TASK_DEFINITION
plugins:
- name: ssm

{{ ssm "/path/to/parameter" }} will retrieve a parameter from the AWS Systems Manager Parameter Store.

This function supports String, StringList, and SecureString types.

Pitfalls

Rule Name Uniqueness and Overwrite Risks

ecschedule is designed to guarantee the uniqueness of job definitions by rule name in the configuration file.

If ecschedule is run in an environment where a Rule that is not managed by ecschedule already exists, ecschedule will overwrite that Rule. If you do not intend to overwrite, please ensure that the names written in the configuration file do not duplicate with existing Rules.

Note on Previous Versions

In versions v0.9.1 and earlier, there were issues related to rule name changes causing garbage definitions and rules not being deleted from AWS when removed from the configuration file. These issues have been addressed in version v0.10.0 with the introduction of the -prune option.

Author

Songmu

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func GetAWSAccountID

func GetAWSAccountID(conf aws.Config) (string, error)

GetAWSAccountID returns id

func Run

func Run(ctx context.Context, argv []string, outStream, errStream io.Writer) error

Run the ecschedule

Types

type AwsVpcConfiguration added in v0.3.0

type AwsVpcConfiguration struct {
	Subnets        []string `yaml:"subnets" json:"subnets"`
	SecurityGroups []string `yaml:"security_groups,omitempty" json:"security_groups,omitempty"`
	AssignPublicIP string   `yaml:"assign_public_ip,omitempty" json:"assign_public_ip,omitempty"`
}

AwsVpcConfiguration represents AWS VPC configuration

type BaseConfig

type BaseConfig struct {
	Region     string `yaml:"region" json:"region"`
	Cluster    string `yaml:"cluster" json:"cluster"`
	AccountID  string `yaml:"-" json:"-"`
	TrackingID string `yaml:"trackingId,omitempty" json:"trackingId,omitempty"`
}

BaseConfig baseconfig

type CapacityProviderStrategyItem added in v0.12.0

type CapacityProviderStrategyItem struct {
	CapacityProvider string `yaml:"capacityProvider" json:"capacityProvider"`
	Base             int32  `yaml:"base" json:"base"`
	Weight           int32  `yaml:"weight" json:"weight"`
}

CapacityProviderStrategyItem represents ECS capacity provider strategy item

type Config

type Config struct {
	Role        string `yaml:"role,omitempty" json:"role,omitempty"`
	*BaseConfig `yaml:",inline" json:",inline"`
	Rules       []*Rule   `yaml:"rules" json:"rules"`
	Plugins     []*Plugin `yaml:"plugins,omitempty" json:"plugins,omitempty"`
	// contains filtered or unexported fields
}

Config config

func LoadConfig

func LoadConfig(ctx context.Context, r io.Reader, accountID string, confPath string) (*Config, error)

LoadConfig loads config

func (*Config) GetRuleByName

func (c *Config) GetRuleByName(name string) *Rule

GetRuleByName gets rule by name

type ContainerOverride

type ContainerOverride struct {
	Name              string            `yaml:"name" json:"name"`
	Command           []string          `yaml:"command,flow" json:"command"` // ,flow
	Environment       map[string]string `yaml:"environment,omitempty" json:"environment,omitempty"`
	Cpu               *int32            `yaml:"cpu,omitempty" json:"cpu,omitempty"`
	Memory            *int32            `yaml:"memory,omitempty" json:"memory,omitempty"`
	MemoryReservation *int32            `yaml:"memoryReservation,omitempty" json:"memoryReservation,omitempty"`
}

ContainerOverride overrides container

type DeadLetterConfig added in v0.4.0

type DeadLetterConfig struct {
	Sqs string `yaml:"sqs" json:"sqs"`
}

A DeadLetterConfig object that contains information about a dead-letter queue configuration.

type NetworkConfiguration added in v0.3.0

type NetworkConfiguration struct {
	AwsVpcConfiguration *AwsVpcConfiguration `yaml:"aws_vpc_configuration" json:"aws_vpc_configuration"`
}

NetworkConfiguration represents ECS network configuration

type Plugin added in v0.5.0

type Plugin struct {
	Name       string                 `yaml:"name"`
	Config     map[string]interface{} `yaml:"config"`
	FuncPrefix string                 `yaml:"func_prefix,omitempty"`
}

Plugin the plugin

type Query added in v0.10.0

type Query struct {
	ResourceTypeFIlters []string    `json:"ResourceTypeFilters"`
	TagFilters          []TagFilter `json:"TagFilters"`
}

type Rule

type Rule struct {
	Name               string `yaml:"name" json:"name"`
	Description        string `yaml:"description,omitempty" json:"description,omitempty"`
	ScheduleExpression string `yaml:"scheduleExpression" json:"scheduleExpression"`
	Disabled           bool   `yaml:"disabled,omitempty" json:"disabled,omitempty"` // ENABLE | DISABLE
	*Target            `yaml:",inline" json:",inline"`

	*BaseConfig `yaml:",inline,omitempty"`
}

Rule the rule

func NewRuleFromRemote added in v0.10.0

func NewRuleFromRemote(ctx context.Context, awsConf aws.Config, bc *BaseConfig, ruleName string) (*Rule, error)

NewRuleFromRemote creates a new rule from remote (AWS EventBridge Rule)

func (*Rule) Apply

func (r *Rule) Apply(ctx context.Context, awsConf aws.Config, dryRun bool) error

Apply the rule (maintained for backward compatibility)

func (*Rule) Delete added in v0.10.0

func (r *Rule) Delete(ctx context.Context, awsConf aws.Config, dryRun bool) error

Delete the rule (maintained for backward compatibility)

func (*Rule) PutRuleInput

func (r *Rule) PutRuleInput() *cloudwatchevents.PutRuleInput

PutRuleInput puts rule input

func (*Rule) PutTargetsInput

func (r *Rule) PutTargetsInput() *cloudwatchevents.PutTargetsInput

PutTargetsInput puts targets input

func (*Rule) Run

func (r *Rule) Run(ctx context.Context, awsConf aws.Config, noWait bool) error

Run the rule

func (*Rule) TagResourceInput added in v0.10.0

func (r *Rule) TagResourceInput() *cloudwatchevents.TagResourceInput

TagResourceInput tags resource input

type TagFilter added in v0.10.0

type TagFilter struct {
	Key    string   `json:"Key"`
	Values []string `json:"Values"`
}

type Target

type Target struct {
	TargetID                 string                          `yaml:"targetId,omitempty" json:"targetId,omitempty"`
	TaskDefinition           string                          `yaml:"taskDefinition" json:"taskDefinition"`
	TaskCount                int32                           `yaml:"taskCount,omitempty" json:"taskCount,omitempty"`
	TaskOverride             *TaskOverride                   `yaml:"taskOverride,omitempty" json:"taskOverride,omitempty"`
	ContainerOverrides       []*ContainerOverride            `yaml:"containerOverrides,omitempty" json:"containerOverrides,omitempty"`
	Role                     string                          `yaml:"role,omitempty" json:"role,omitempty"`
	Group                    string                          `yaml:"group,omitempty" json:"group,omitempty"`
	CapacityProviderStrategy []*CapacityProviderStrategyItem `yaml:"capacityProviderStrategy,omitempty" json:"capacityProviderStrategy,omitempty"`
	LaunchType               string                          `yaml:"launch_type,omitempty" json:"launch_type,omitempty"`
	PlatformVersion          string                          `yaml:"platform_version,omitempty" json:"platform_version,omitempty"`
	NetworkConfiguration     *NetworkConfiguration           `yaml:"network_configuration,omitempty" json:"network_configuration,omitempty"`
	DeadLetterConfig         *DeadLetterConfig               `yaml:"dead_letter_config,omitempty" json:"dead_letter_config,omitempty"`
	PropagateTags            *string                         `yaml:"propagateTags,omitempty" json:"propagateTags,omitempty"`
}

Target cluster

type TaskOverride added in v0.15.0

type TaskOverride struct {
	Cpu    *string `yaml:"cpu,omitempty" json:"cpu,omitempty"`
	Memory *string `yaml:"memory,omitempty" json:"memory,omitempty"`
}

NOTE: ContainerOverrides should conceptually be inside TaskOverride (cf. https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_TaskOverride.html). However, for backward-compatibility, we keep ContainerOverrides and TaskOverride as separate fields and merge them into a single struct in `apply` and `run`. That's why containerOverrides field is not inside TaskOverride.

Directories

Path Synopsis
cmd
ecschedule command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL