full360-sequencer 0.0.5 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0e85e474f15e14b013eae227a2e7476fee870d031d2906b74e9be5fd60d92d12
4
- data.tar.gz: fc732ffe3f575c2324ee35bb33a1cd1d5c70948a79a600d2985baf603bebf7e8
3
+ metadata.gz: b89f369df62f5fa73c8eef3b7597596bcbe052026761aed9e9c9b1b23bc0fb90
4
+ data.tar.gz: bb4a599e8df5d9cd2410fe5d91fe15a68648731f1bf33c3979708f6dc3f822ed
5
5
  SHA512:
6
- metadata.gz: 5eda39159ea5cb80145574c01b594aba5193a1adb970ce0721c9d4383bae5889e7f059a0712c659aa856b91bf03fc26f127704996bccdc82f3b4faeb849fccf5
7
- data.tar.gz: 85ffa009279ff7a855097902856d3c9fd48bc5b777b53e936360d8a7ba91c61dbd03968540ee95735cd1175660a70bb322d34a2920442004056694e92b061358
6
+ metadata.gz: 7df9df844dabb0d75c55971cc09da599b34ca4796fb670e6a49209ba652bfffdd81d290b9c35c516d9f8513d097a0cec8305eeb739eb68acfeb2a5ae5770c336
7
+ data.tar.gz: 1585bb1dadb01907286f2c7deab022d8f69d7fc2dec9ad51aeaf860885a74b624bffe48677ae159779eb8dd5faee383b19ed93f1f38c03b3b9978feb8c65e02d
@@ -0,0 +1,98 @@
1
+ name: Release Gem
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ jobs:
9
+ release:
10
+ runs-on: ubuntu-latest
11
+
12
+ steps:
13
+ # Checkout code if release was created
14
+ - uses: actions/checkout@v2
15
+
16
+ # Setup ruby if a release was created
17
+ - uses: ruby/setup-ruby@v1
18
+ with:
19
+ ruby-version: 2.7
20
+ bundler-cache: true
21
+
22
+ # Publish
23
+ - name: publish gem
24
+ run: |
25
+ mkdir -p $HOME/.gem
26
+ touch $HOME/.gem/credentials
27
+ chmod 0600 $HOME/.gem/credentials
28
+ printf -- "---\n:rubygems_api_key: ${RUBYGEMS_API_KEY}\n" > $HOME/.gem/credentials
29
+ gem build *.gemspec
30
+ gem push *.gem
31
+ env:
32
+ RUBYGEMS_API_KEY: "${{secrets.RUBYGEMS_API_KEY}}"
33
+
34
+ docker-jruby:
35
+ runs-on: ubuntu-latest
36
+ needs: release
37
+
38
+ steps:
39
+ - name: Checkout
40
+ uses: actions/checkout@v2
41
+
42
+ - name: Set up Docker Buildx
43
+ uses: docker/setup-buildx-action@v1
44
+
45
+ - name: Login to DockerHub
46
+ uses: docker/login-action@v1
47
+ with:
48
+ username: ${{ secrets.DOCKER_HUB_USERNAME }}
49
+ password: ${{ secrets.DOCKER_HUB_TOKEN }}
50
+
51
+ - name: Extract tag name
52
+ id: tag
53
+ uses: actions/github-script@0.2.0
54
+ with:
55
+ script: return context.payload.ref.replace(/\/refs\/tags\//, '')
56
+
57
+ - name: Build JRuby image
58
+ uses: docker/build-push-action@v2
59
+ with:
60
+ file: ./Dockerfile.jruby
61
+ pull: true
62
+ push: true
63
+ tags: |
64
+ full360/sequencer:jruby-latest
65
+ full360/sequencer:${{ steps.tag.outputs.result }}-jruby-latest
66
+
67
+ docker-ruby:
68
+ runs-on: ubuntu-latest
69
+ needs: release
70
+
71
+ steps:
72
+ - name: Checkout
73
+ uses: actions/checkout@v2
74
+
75
+ - name: Set up Docker Buildx
76
+ uses: docker/setup-buildx-action@v1
77
+
78
+ - name: Login to DockerHub
79
+ uses: docker/login-action@v1
80
+ with:
81
+ username: ${{ secrets.DOCKER_HUB_USERNAME }}
82
+ password: ${{ secrets.DOCKER_HUB_TOKEN }}
83
+
84
+ - name: Extract tag name
85
+ id: tag
86
+ uses: actions/github-script@0.2.0
87
+ with:
88
+ script: return context.payload.ref.replace(/\/refs\/tags\//, '')
89
+
90
+ - name: Build Ruby image
91
+ uses: docker/build-push-action@v2
92
+ with:
93
+ file: ./Dockerfile.ruby
94
+ pull: true
95
+ push: true
96
+ tags: |
97
+ full360/sequencer:ruby-latest
98
+ full360/sequencer:${{ steps.tag.outputs.result }}-ruby-latest
@@ -0,0 +1,26 @@
1
+ name: CI Test
2
+
3
+ on: [push, pull_request]
4
+
5
+ jobs:
6
+ test:
7
+ strategy:
8
+ fail-fast: false
9
+ matrix:
10
+ os: [ubuntu-latest, macos-latest]
11
+ # Due to https://github.com/actions/runner/issues/849, we have to use quotes for '3.0'
12
+ ruby: ["2.7", "jruby-9.2.19.0", "jruby-9.3.0.0"]
13
+ runs-on: ${{ matrix.os }}
14
+
15
+ if: "!contains(github.event.head_commit.message, '[ci skip]')"
16
+
17
+ steps:
18
+ - name: Checkout
19
+ uses: actions/checkout@v2
20
+
21
+ - name: Test ${{ matrix.ruby }}
22
+ uses: ruby/setup-ruby@v1
23
+ with:
24
+ ruby-version: ${{ matrix.ruby }}
25
+ bundler-cache: true # runs 'bundle install' and caches installed gems automatically
26
+ - run: bundle exec rake
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ .DS_Store
2
+ .ruby-version
3
+
4
+ /.bundle/
5
+ /.yardoc
6
+ /_yardoc/
7
+ /coverage/
8
+ /doc/
9
+ /pkg/
10
+ /spec/reports/
11
+ /tmp/
12
+
13
+ # rspec failure tracking
14
+ .rspec_status
15
+
16
+ # generated gems
17
+ *.gem
data/CHANGELOG.md ADDED
@@ -0,0 +1,57 @@
1
+ # Changelog
2
+ All notable changes to this project will be documented in this file.
3
+
4
+ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
5
+ and this project adheres to [Semantic
6
+ Versioning](http://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+ ### Added
10
+ ### Changed
11
+ ### Removed
12
+
13
+ ## 0.2.0
14
+ ### Added
15
+ - Add Docker image for Ruby and JRuby that will get triggered after the release.
16
+ ### Changed
17
+ - Refactor the code to make it easier to read and modify.
18
+ - Update the AWS SDK for ECS to V3.
19
+ - Fix a missing logger dependency in the bin/sequencer code.
20
+ ### Removed
21
+ - Tests for Ruby 3.0 as they were failing for an unknown reason and I couldn't
22
+ reproduce locally.
23
+
24
+ ## 0.1.3
25
+ ### Added
26
+ - Fix release workflow.
27
+ ### Changed
28
+ ### Removed
29
+
30
+ ## 0.1.2
31
+ ### Added
32
+ - Fix release workflow.
33
+ ### Changed
34
+ ### Removed
35
+
36
+ ## 0.1.1
37
+ ### Added
38
+ - Workflows dependable.
39
+ ### Changed
40
+ ### Removed
41
+
42
+ ## 0.1.0
43
+ ### Added
44
+ - Working GitHub Actions workflow for tests and releases.
45
+ - Add Gemfile to the Gem.
46
+ ### Changed
47
+ - Update the .gemspec file and dependencies.
48
+ - Update the gem file format.
49
+ - Change from single quote to double quotes.
50
+ - Fix deprecation warnings in tests
51
+ ### Removed
52
+
53
+ ## 0.0.7
54
+ ### Added
55
+ - Working version.
56
+ ### Changed
57
+ ### Removed
data/Dockerfile.jruby ADDED
@@ -0,0 +1,23 @@
1
+ FROM jruby:9.2-jre8
2
+
3
+ # Should be more than sufficient for transforms without large recordset
4
+ # operations.
5
+ ENV JRUBY_OPTS=-J-Xmx1024m
6
+
7
+ RUN apt-get update \
8
+ && apt-get install -y --no-install-recommends \
9
+ unzip \
10
+ ca-certificates \
11
+ awscli \
12
+ && update-ca-certificates \
13
+ && rm -rf /var/lib/apt/lists/*
14
+
15
+ #set time zone
16
+ RUN mv /etc/localtime /etc/localtime.bak ; ln -s /usr/share/zoneinfo/UTC /etc/localtime
17
+
18
+ RUN gem install full360-sequencer
19
+
20
+ # this is the entry point
21
+ ADD entrypoint.sh /usr/sbin/runner.sh
22
+ RUN chmod 755 /usr/sbin/runner.sh
23
+ ENTRYPOINT ["/usr/sbin/runner.sh"]
data/Dockerfile.ruby ADDED
@@ -0,0 +1,21 @@
1
+ FROM ruby:2.7-alpine
2
+
3
+ # Install required packages
4
+ RUN apk add --no-cache \
5
+ unzip \
6
+ ca-certificates \
7
+ bash \
8
+ wget \
9
+ curl \
10
+ aws-cli \
11
+ && update-ca-certificates \
12
+
13
+ #set time zone
14
+ RUN mv /etc/localtime /etc/localtime.bak ; ln -s /usr/share/zoneinfo/UTC /etc/localtime
15
+
16
+ RUN gem install full360-sequencer
17
+
18
+ # this is the entry point
19
+ ADD entrypoint.sh /usr/sbin/runner.sh
20
+ RUN chmod 755 /usr/sbin/runner.sh
21
+ ENTRYPOINT ["/usr/sbin/runner.sh"]
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,52 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ full360-sequencer (0.2.0)
5
+ aws-sdk-ecs (~> 1.85)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ aws-eventstream (1.2.0)
11
+ aws-partitions (1.509.0)
12
+ aws-sdk-core (3.121.1)
13
+ aws-eventstream (~> 1, >= 1.0.2)
14
+ aws-partitions (~> 1, >= 1.239.0)
15
+ aws-sigv4 (~> 1.1)
16
+ jmespath (~> 1.0)
17
+ aws-sdk-ecs (1.85.0)
18
+ aws-sdk-core (~> 3, >= 3.120.0)
19
+ aws-sigv4 (~> 1.1)
20
+ aws-sigv4 (1.4.0)
21
+ aws-eventstream (~> 1, >= 1.0.2)
22
+ coderay (1.1.3)
23
+ ffi (1.15.4-java)
24
+ jmespath (1.4.0)
25
+ method_source (1.0.0)
26
+ minitest (5.14.4)
27
+ pry (0.14.1)
28
+ coderay (~> 1.1)
29
+ method_source (~> 1.0)
30
+ pry (0.14.1-java)
31
+ coderay (~> 1.1)
32
+ method_source (~> 1.0)
33
+ spoon (~> 0.0)
34
+ rake (12.3.3)
35
+ spoon (0.0.6)
36
+ ffi
37
+
38
+ PLATFORMS
39
+ universal-java-11
40
+ universal-java-8
41
+ x86_64-darwin-18
42
+ x86_64-darwin-19
43
+ x86_64-linux
44
+
45
+ DEPENDENCIES
46
+ full360-sequencer!
47
+ minitest (~> 5.9)
48
+ pry (~> 0.14)
49
+ rake (~> 12)
50
+
51
+ BUNDLED WITH
52
+ 2.2.28
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2017 Full 360 Inc
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,104 @@
1
+ # Full 360 Sequencer
2
+
3
+ Sequencer is a tool that supports the synchronous execution of docker containers
4
+ running in Amazon ECS for the purpose of batch processing.
5
+
6
+ While there are many solutions for container orchestration, most of them are
7
+ focused on managing and scaling long running microservices, where a group of
8
+ containers need to kept alive and able to talk to each other. This model is
9
+ quite different from the execution of a nightly ETL batch where a container
10
+ performs a series of actions such as:
11
+
12
+ * collect the data from source system
13
+ * transform the data into the data lake
14
+ * ingest the data into the database
15
+ * transform the data inside the database with SQL
16
+ * export data to downstream consumers such as third parties or BI tools
17
+
18
+ AWS Batch service could do the above but it is currently only available in a
19
+ single region.
20
+
21
+ At one end of the spectrum your other options fall into the realm of shell
22
+ scripts.. while the other end brings you powerful (and cool!) DAG based tools
23
+ such as Nomad and Airflow.
24
+
25
+ We consider the DAG based tools to be way overkill for a simple batch of
26
+ synchronous jobs, so we opted to expand on the idea of a shell script.
27
+
28
+ Putting a shell script into a container is easy enough, but it requires that you
29
+ build and deploy a new container every time you make a change.
30
+
31
+ Sequencer allows you to define a list of ECS tasks in a yaml file, the location
32
+ of which is passed into the container at run time. This yaml file can sit in an
33
+ S3 repository (git support forthcoming). Sequencer will synchronously run each
34
+ task, passing in your override parameters as specified.
35
+
36
+ ## Installing Sequencer
37
+
38
+ Sequencer is installed as a ruby gem:
39
+
40
+ gem install full360-sequencer
41
+
42
+ Once installed... you can run the sequencer command from anywhere on your
43
+ system. Simply pass the path to your YAML file as a parameter. Note that you
44
+ will need to export some environment variables in order to use the AWS API.
45
+
46
+ $ export AWS_ACCESS_KEY_ID=aaaaaaaaayourkey
47
+ $ export AWS_SECRET_ACCESS_KEY=bbbbbbbbbbbbbbbbbbbbbbbbbbbbyoursecret
48
+ $ export AWS_REGION=us-west-2
49
+ $ sequencer mybatch.yml
50
+
51
+ Sequencer will utilize AWS instance or container roles if appropriate, though
52
+ you will have to provide `AWS_REGION` if you are not in us-east-1.
53
+
54
+ While using the command line sequencer is just fine, we suggest that you use the
55
+ full360/sequencer container available on Dockerhub.
56
+
57
+ ## YAML File Specification
58
+
59
+ Sequencer requires a yaml file with the following structure:
60
+
61
+ ```yaml
62
+ - first_task:
63
+ type: ecs_task
64
+ parameters:
65
+ cluster: yourcluster
66
+ task_definition: test-task-def:1
67
+ count: 1
68
+ - first_task:
69
+ type: ecs_task
70
+ parameters:
71
+ cluster: yourcluster2
72
+ task_definition: test-task-def:2
73
+ count: 1
74
+ ```
75
+
76
+ The top level structure is an array of tasks to be executed in the order shown.
77
+ Each task can be provided a name. Below each task you must specify the task
78
+ `type`. At this time, only ecs_task is supported as a task type.
79
+
80
+ The `parameters` element correlates to the request provided to the ECS run_task
81
+ API call. All of the elements are supported as long as you provide a YAML
82
+ structure. Refer to the API [reference][ref] for more details.
83
+
84
+ ## Releasing
85
+
86
+ Releasing a new version of the Gem requires a few steps:
87
+ - Update the `CHANGELOG.md` file
88
+ - Add a new section for the [UNRELEASED] code
89
+ - Check that all changes are reflected for the current release version
90
+ - Update the `version.rb` file
91
+ - Do a bundle install to ensure we are locking the latest version
92
+ - Commit all the changes
93
+ - Example: `git commit -m "Bump gem to version 1.0.0"`
94
+ - Create a Git tag that matches the version number in `version.rb`
95
+ - Example: `git tag -m "Version 1.0.0" v1.0.0`
96
+
97
+ ## Running the Sequencer from Docker
98
+
99
+ Pass the following environment variables to the container at runtime:
100
+
101
+ * `SEQUENCER_YAML_S3_PATH` - s3:// path to the yaml configuration file.
102
+ * `AWS_REGION` - AWS region in which you are running the container.
103
+
104
+ [ref]: http://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_RunTask.html
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ task :test do
4
+ Dir["./test/*_test.rb"].each { |file| require file}
5
+ end
6
+
7
+ task default: %w(test)
data/bin/console ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "pry"
5
+ require "full360-sequencer"
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ Pry.start
data/bin/sequencer CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require 'full360-sequencer'
3
+ require "logger"
4
+ require "full360-sequencer"
4
5
 
5
6
  config_file = ARGV[0]
6
7
 
@@ -14,29 +15,29 @@ def logger
14
15
  end
15
16
 
16
17
  def sequencer_version
17
- Gem.loaded_specs['full360-sequencer'].version
18
+ Full360::Sequencer::VERSION
18
19
  end
19
20
 
20
21
  begin
21
22
  logger.info("sequencer version #{sequencer_version}")
22
- logger.level = ENV['SEQUENCER_LOG_DEBUG'] ? Logger::DEBUG : Logger::INFO
23
+ logger.level = ENV["SEQUENCER_LOG_DEBUG"] ? Logger::DEBUG : Logger::INFO
23
24
 
24
25
  if config_file == nil
25
- logger.error('SEQUENCER_ERROR')
26
- logger.error('YAML file not provided... exiting with error code 1')
26
+ logger.error("SEQUENCER_ERROR: YAML file not provided... exiting with error code 1")
27
27
  exit 1
28
28
  else
29
- r = Full360::Sequencer::Runner.new(logger)
30
- r.config_from_file(config_file)
31
- r.sleep_between_checks = ENV['SEQUENCER_SLEEP_BETWEEN_CHECKS'].to_i if ENV['SEQUENCER_SLEEP_BETWEEN_CHECKS']
32
- r.run
29
+ sleep_between_checks = ENV.fetch("SEQUENCER_SLEEP_BETWEEN_CHECKS", 5).to_i
30
+
31
+ Full360::Sequencer::Runner.new(sleep_between_checks, logger).tap do |runner|
32
+ runner.config_from_file(config_file)
33
+ runner.run
34
+ end
33
35
  end
34
- logger.info('all steps succeeded... exiting with code 0')
36
+ logger.info("all steps succeeded... exiting with code 0")
35
37
  exit 0
36
38
  rescue => e
37
- logger.error('SEQUENCER_ERROR')
38
- logger.error(e.message)
39
+ logger.error("SEQUENCER_ERROR: #{e.message}")
39
40
  e.backtrace.each { |r| logger.error(r) }
40
- logger.error('failure... exiting with code 1')
41
+ logger.error("failure... exiting with code 1")
41
42
  exit 1
42
- end
43
+ end
data/entrypoint.sh ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env bash
2
+
3
+ if [ -n "$AWS_REGION" ]
4
+ then
5
+ aws s3 cp $SEQUENCER_YAML_S3_PATH /sequencer/file.yml --region $AWS_REGION
6
+ else
7
+ aws s3 cp $SEQUENCER_YAML_S3_PATH /sequencer/file.yml
8
+ fi
9
+
10
+ sequencer /sequencer/file.yml
@@ -0,0 +1,34 @@
1
+ lib = File.expand_path("../lib", __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require "full360_sequencer/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "full360-sequencer"
7
+ spec.version = Full360::Sequencer::VERSION
8
+ spec.date = "2017-10-17"
9
+ spec.summary = "Full 360 sequencer utility"
10
+ spec.description = "Automation for simple batch jobs run in AWS"
11
+ spec.authors = ["Full 360 Group"]
12
+ spec.email = "support@full360.com"
13
+ spec.files = ["lib/full360-sequencer.rb"]
14
+ spec.homepage = "https://full360.com"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
+ f.match(%r{^(test|spec|features)/})
19
+ end
20
+
21
+ spec.bindir = "bin"
22
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
23
+ spec.require_paths = ["lib"]
24
+
25
+ # Make it Ruby 2+ only
26
+ spec.required_ruby_version = ">= 2.0"
27
+
28
+ spec.add_runtime_dependency "aws-sdk-ecs", "~> 1.85"
29
+
30
+ # development dependencies
31
+ spec.add_development_dependency "minitest", "~> 5.9"
32
+ spec.add_development_dependency "rake", "~> 12"
33
+ spec.add_development_dependency "pry", "~> 0.14"
34
+ end
@@ -1,158 +1 @@
1
- #!/usr/bin/ruby
2
- require 'aws-sdk'
3
- require 'yaml'
4
- require 'logger'
5
- require 'pp'
6
-
7
- module Full360
8
- module Sequencer
9
- class Runner
10
- attr_accessor :sleep_between_checks
11
- attr_accessor :config
12
-
13
- def initialize(logger = nil)
14
- @logger = logger ? logger : Logger.new(STDOUT)
15
-
16
- # default 5 seconds between completed? checks
17
- @sleep_between_checks = 5
18
- end
19
-
20
- def config_from_file(yaml_path)
21
- @config = parse_config_file(yaml_path)
22
- end
23
-
24
- def run_task_class(task_type_string)
25
- case task_type_string
26
- when 'ecs_task' then Full360::Sequencer::RunECSTask
27
- else nil
28
- end
29
- end
30
-
31
- def run
32
- @config.each do |params|
33
- this_task_name = task_name(params)
34
- this_task = run_task_class(params[this_task_name]['type']).new(
35
- this_task_name,
36
- params[this_task_name]
37
- )
38
- this_task.run_task
39
- until this_task.completed?
40
- sleep @sleep_between_checks
41
- end
42
- raise "task failed error" unless this_task.success
43
- end
44
- rescue => e
45
- @logger.error('SEQUENCER_ERROR')
46
- @logger.error(e.message)
47
- e.backtrace.each { |r| @logger.error(r) }
48
- end
49
-
50
- def task_name(params)
51
- params.keys.first
52
- end
53
-
54
- def parse_config_file(yaml_path)
55
- YAML.load_file(yaml_path)
56
- end
57
-
58
- def config_valid?(config)
59
- return false unless config.is_a? Array
60
- true
61
- end
62
- end
63
-
64
- class RunTaskBase
65
- attr_reader :success
66
- attr_reader :exit_code
67
-
68
- def run_task; end
69
- def completed?; end
70
- def kill_task; end #will be used for timeout
71
- end
72
-
73
- class RunECSTask < RunTaskBase
74
- def initialize(task_name, params, logger = nil)
75
- @logger = logger ? logger : Logger.new(STDOUT)
76
- @task_name = task_name
77
- @params = params['parameters']
78
- @params = keys_to_symbol(@params)
79
- @cluster = @params[:cluster]
80
- end
81
-
82
- def keys_to_symbol(params)
83
- # replaces string keys with symbol keys
84
- # required by AWS SDK
85
- if params.is_a?(Hash)
86
- params.inject({}){ |memo,(k,v)| memo[k.to_sym] = v; memo }
87
- else
88
- nil
89
- end
90
- end
91
-
92
- def run_task
93
- @logger.info("starting ECS task #{@task_name}")
94
- resp = ecs_run_task
95
- @task_arn = resp.tasks[0].task_arn
96
- @logger.info("#{@task_name} task created #{@task_arn} on cluster #{@cluster}")
97
- end
98
-
99
- def ecs_run_task
100
- @logger.debug("creating AWS client for ECS task #{@task_name}...")
101
- @ecs_client = ::Aws::ECS::Client.new
102
- @logger.debug("running ECS task #{@task_name}...")
103
- @start_time = Time.new
104
- resp = @ecs_client.run_task(@params)
105
- return resp
106
- rescue => e
107
- @logger.error('SEQUENCER_ERROR')
108
- @logger.error("error creating ECS task...")
109
- @logger.error("response from ECS: #{resp}")
110
- raise e
111
- end
112
-
113
- def ecs_describe_tasks
114
- @ecs_client.describe_tasks(
115
- {
116
- cluster: @cluster,
117
- tasks: [@task_arn] # required
118
- }
119
- )
120
- end
121
-
122
- def completed?
123
- resp = ecs_describe_tasks
124
- status = last_task_status(resp)
125
- @logger.info("#{@task_name} : #{@task_arn} current status: #{status}")
126
- if status == 'STOPPED'
127
- @logger.info("#{@task_name} completed in #{Time.new - @start_time} seconds")
128
- # parse exit_code(s) and return completion
129
- @success = determine_success(resp)
130
- return true
131
- end
132
- false
133
- rescue => e
134
- @logger.error('SEQUENCER_ERROR')
135
- @logger.error(e.message)
136
- e.backtrace.each { |r| @logger.error(r) }
137
- end
138
-
139
- # parses last status from aws API response
140
- def last_task_status(resp)
141
- resp.tasks[0].last_status
142
- end
143
-
144
- # success is determined by all containers having zero exit code
145
- def determine_success(resp)
146
- success = true
147
- resp.tasks[0].containers.each do |c|
148
- @logger.info("#{@task_name} : container #{c.name} #{c.container_arn} completed with exit_code #{c.exit_code}")
149
- if c.exit_code != 0
150
- # we had a problem!
151
- success = false
152
- end
153
- end
154
- success
155
- end
156
- end
157
- end
158
- end
1
+ require "full360_sequencer"
@@ -0,0 +1,103 @@
1
+ require "logger"
2
+ require "aws-sdk-ecs"
3
+
4
+ module Full360
5
+ module Sequencer
6
+ class RunECSTask < RunTaskBase
7
+ attr_accessor :task_name
8
+ attr_accessor :params
9
+ attr_accessor :ecs_client
10
+ attr_accessor :logger
11
+
12
+ attr_reader :cluster
13
+ attr_reader :task_arn
14
+ attr_reader :start_time
15
+
16
+ def initialize(task_name, params, ecs_client = nil, logger = nil)
17
+ @logger = logger ||= Logger.new(STDOUT)
18
+ @ecs_client = ecs_client ||= Aws::ECS::Client.new
19
+ @task_name = task_name
20
+ @params = params[:parameters]
21
+ @cluster = self.params[:cluster]
22
+ end
23
+
24
+ def run_task
25
+ logger.info("starting ECS task #{task_name}")
26
+
27
+ resp = ecs_run_task
28
+ @task_arn = resp.tasks.first.task_arn
29
+
30
+ logger.info("#{task_name} task created #{task_arn} on cluster #{cluster}")
31
+ end
32
+
33
+ def ecs_run_task
34
+ logger.debug("running ECS task #{task_name}...")
35
+ @start_time = Time.new.utc
36
+
37
+ resp = ecs_client.run_task(params)
38
+ resp
39
+ rescue => e
40
+ logger.error("SEQUENCER_ERROR: response from ECS #{resp}")
41
+ raise e
42
+ end
43
+
44
+ def ecs_describe_tasks
45
+ ecs_client.describe_tasks(
46
+ {
47
+ cluster: cluster,
48
+ tasks: [task_arn],
49
+ }
50
+ )
51
+ end
52
+
53
+ def completed?
54
+ retries ||= 0
55
+
56
+ resp = ecs_describe_tasks
57
+ status = last_task_status(resp)
58
+
59
+ logger.info("#{task_name}: #{task_arn} current status: #{status}")
60
+
61
+ completed = false
62
+
63
+ if status == "STOPPED"
64
+ logger.info("#{task_name} completed in #{Time.new.utc - start_time} seconds")
65
+ # parse exit_code(s) and return completion
66
+ @success = determine_success(resp)
67
+ completed = true
68
+ end
69
+
70
+ completed
71
+ rescue => e
72
+ logger.warn(e.message)
73
+ logger.warn("task completion check failed, trying again ##{retries}")
74
+
75
+ sleep 10*retries
76
+
77
+ retry if (retries += 1) < 3
78
+
79
+ logger.error("SEQUENCER_ERROR: #{e.message}")
80
+ e.backtrace.each { |r| logger.error(r) }
81
+ end
82
+
83
+ # parses last status from aws API response
84
+ def last_task_status(resp)
85
+ resp.tasks.first.last_status
86
+ end
87
+
88
+ # success is determined by all containers having zero exit code
89
+ def determine_success(resp)
90
+ success = true
91
+
92
+ resp.tasks.first.containers.each do |c|
93
+ logger.info("#{task_name}: container #{c.name} #{c.container_arn} completed with exit_code #{c.exit_code}")
94
+
95
+ # we had a problem!
96
+ success = false if c.exit_code != 0
97
+ end
98
+
99
+ success
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,12 @@
1
+ module Full360
2
+ module Sequencer
3
+ class RunTaskBase
4
+ attr_reader :success
5
+ attr_reader :exit_code
6
+
7
+ def run_task; end
8
+ def completed?; end
9
+ def kill_task; end # will be used for timeout
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,79 @@
1
+ require "yaml"
2
+ require "logger"
3
+ require "full360_sequencer/run_ecs_task"
4
+
5
+ module Full360
6
+ module Sequencer
7
+ class Runner
8
+ attr_accessor :sleep_between_checks
9
+ attr_accessor :logger
10
+
11
+ attr_reader :config
12
+
13
+ # Initializes the class
14
+ # @params sleep_between_checks [Int]
15
+ # @params logger [Logger]
16
+ # @return [Full360::Sequencer::Runner]
17
+ def initialize(sleep_between_checks, logger = nil)
18
+ @sleep_between_checks = sleep_between_checks
19
+ @logger = logger ||= Logger.new(STDOUT)
20
+ end
21
+
22
+ def config_from_file(yaml_path)
23
+ @config = parse_config_file(yaml_path)
24
+ end
25
+
26
+ def run_task_class(task_type_string)
27
+ case task_type_string
28
+ when "ecs_task"
29
+ Full360::Sequencer::RunECSTask
30
+ else
31
+ nil
32
+ end
33
+ end
34
+
35
+ def run
36
+ config.each do |params|
37
+ this_task_name = task_name(params)
38
+
39
+ this_task = run_task_class(params[this_task_name][:type]).new(
40
+ this_task_name,
41
+ params[this_task_name]
42
+ )
43
+
44
+ this_task.run_task
45
+
46
+ until this_task.completed?
47
+ sleep sleep_between_checks
48
+ end
49
+
50
+ raise "task failed error" unless this_task.success
51
+ end
52
+ rescue => e
53
+ logger.error("SEQUENCER_ERROR: #{e.message}")
54
+
55
+ e.backtrace.each { |r| logger.error(r) }
56
+ raise e
57
+ end
58
+
59
+ def task_name(params)
60
+ params.keys.first
61
+ end
62
+
63
+ def config_valid?(config)
64
+ return false unless config.is_a? Array
65
+ true
66
+ end
67
+
68
+ private
69
+
70
+ # parse_config_file reads and parses the configuration file and returns
71
+ # an array of hashes with symbolize keys ready to consume.
72
+ # @params yaml_path [String]
73
+ # @return [Array]
74
+ def parse_config_file(yaml_path)
75
+ YAML.safe_load(File.read(yaml_path), symbolize_names: true)
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,5 @@
1
+ module Full360
2
+ module Sequencer
3
+ VERSION = "0.2.0".freeze
4
+ end
5
+ end
@@ -0,0 +1,11 @@
1
+ require "logger"
2
+
3
+ # Autoload all gem classes
4
+ module Full360
5
+ module Sequencer
6
+ autoload :VERSION, "full360_sequencer/version"
7
+ autoload :Runner, "full360_sequencer/runner"
8
+ autoload :RunECSTask, "full360_sequencer/run_ecs_task"
9
+ autoload :RunTaskBase, "full360_sequencer/run_task_base"
10
+ end
11
+ end
metadata CHANGED
@@ -1,72 +1,105 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: full360-sequencer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
- - pankaj batra
8
- - jeremy winters
9
- autorequire:
7
+ - Full 360 Group
8
+ autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2017-10-03 00:00:00.000000000 Z
11
+ date: 2017-10-17 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
14
+ name: aws-sdk-ecs
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.2'
20
- name: logger
21
- prerelease: false
19
+ version: '1.85'
22
20
  type: :runtime
21
+ prerelease: false
23
22
  version_requirements: !ruby/object:Gem::Requirement
24
23
  requirements:
25
24
  - - "~>"
26
25
  - !ruby/object:Gem::Version
27
- version: '1.2'
26
+ version: '1.85'
28
27
  - !ruby/object:Gem::Dependency
28
+ name: minitest
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '2.9'
34
- name: aws-sdk
33
+ version: '5.9'
34
+ type: :development
35
35
  prerelease: false
36
- type: :runtime
37
36
  version_requirements: !ruby/object:Gem::Requirement
38
37
  requirements:
39
38
  - - "~>"
40
39
  - !ruby/object:Gem::Version
41
- version: '2.9'
40
+ version: '5.9'
42
41
  - !ruby/object:Gem::Dependency
42
+ name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '5.9'
48
- name: minitest
47
+ version: '12'
48
+ type: :development
49
49
  prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '12'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.14'
50
62
  type: :development
63
+ prerelease: false
51
64
  version_requirements: !ruby/object:Gem::Requirement
52
65
  requirements:
53
66
  - - "~>"
54
67
  - !ruby/object:Gem::Version
55
- version: '5.9'
56
- description: automation for simple batch jobs run in AWS
57
- email: pankaj.batra@full360.com
68
+ version: '0.14'
69
+ description: Automation for simple batch jobs run in AWS
70
+ email: support@full360.com
58
71
  executables:
72
+ - console
59
73
  - sequencer
60
74
  extensions: []
61
75
  extra_rdoc_files: []
62
76
  files:
77
+ - ".github/workflows/release.yml"
78
+ - ".github/workflows/test.yml"
79
+ - ".gitignore"
80
+ - CHANGELOG.md
81
+ - Dockerfile.jruby
82
+ - Dockerfile.ruby
83
+ - Gemfile
84
+ - Gemfile.lock
85
+ - LICENSE
86
+ - README.md
87
+ - Rakefile
88
+ - bin/console
63
89
  - bin/sequencer
90
+ - entrypoint.sh
91
+ - full360-sequencer.gemspec
64
92
  - lib/full360-sequencer.rb
65
- homepage: https://www.full360.com
93
+ - lib/full360_sequencer.rb
94
+ - lib/full360_sequencer/run_ecs_task.rb
95
+ - lib/full360_sequencer/run_task_base.rb
96
+ - lib/full360_sequencer/runner.rb
97
+ - lib/full360_sequencer/version.rb
98
+ homepage: https://full360.com
66
99
  licenses:
67
100
  - MIT
68
101
  metadata: {}
69
- post_install_message:
102
+ post_install_message:
70
103
  rdoc_options: []
71
104
  require_paths:
72
105
  - lib
@@ -81,9 +114,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
81
114
  - !ruby/object:Gem::Version
82
115
  version: '0'
83
116
  requirements: []
84
- rubyforge_project:
85
- rubygems_version: 2.6.11
86
- signing_key:
117
+ rubygems_version: 3.1.6
118
+ signing_key:
87
119
  specification_version: 4
88
- summary: full360 sequencer utility
120
+ summary: Full 360 sequencer utility
89
121
  test_files: []