mistral_client 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0746e5658fe10edda68289b63a11f8e8306f939d
4
+ data.tar.gz: fb5750de1c4bc90795b0b3bc49e06267f284c1d1
5
+ SHA512:
6
+ metadata.gz: 15483eea25a2f62af9a40b7efce8d7bfdaa2b1896ce905b2d55d48655114805ee9b29fb350d28bb28e827f9830ddf0f65dc500e50b38d8b878716eaad5665847
7
+ data.tar.gz: f246cac33c6292e134d8f1f56805ef90bf3721fd7987316085733293f6683e1dcbb62627de47c86267345cf91a379d1845ce3e440e567c836024bf257cb164d2
data/.gitignore ADDED
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+
11
+ # rspec failure tracking
12
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/.rubocop.yml ADDED
@@ -0,0 +1,10 @@
1
+ Style/Documentation:
2
+ Enabled: false
3
+
4
+ Style/FrozenStringLiteralComment:
5
+ Enabled: false
6
+
7
+ Metrics/BlockLength:
8
+ Enabled: true
9
+ Exclude:
10
+ - 'spec/**/*'
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.4.2
data/.travis.yml ADDED
@@ -0,0 +1,10 @@
1
+ language: ruby
2
+ cache: bundler
3
+ rvm:
4
+ - 2.3.5
5
+ - 2.4.2
6
+ before_install:
7
+ - rvm @global do gem install bundler
8
+ branches:
9
+ only:
10
+ - master
data/CHANGELOG.md ADDED
@@ -0,0 +1,12 @@
1
+ # Change Log
2
+
3
+ All notable changes to this project will be documented in this file.
4
+ This project adheres to [Semantic Versioning](http://semver.org/).
5
+
6
+ ## [Unreleased]
7
+
8
+ ## 1.0.0 - 2017-10-09
9
+
10
+ * Initial Release
11
+
12
+ [Unreleased]: https://github.com/civisanalytics/mistral_client/compare/v1.0.0...HEAD
@@ -0,0 +1,75 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of
9
+ experience, nationality, personal appearance, race, religion, or sexual identity
10
+ and orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or reject
41
+ comments, commits, code, wiki edits, issues, and other contributions that are
42
+ not aligned to this Code of Conduct, or to ban temporarily or permanently any
43
+ contributor for other behaviors that they deem inappropriate, threatening,
44
+ offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at opensource@civisanalytics.com. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an
62
+ incident. Further details of specific enforcement policies may be posted
63
+ separately.
64
+
65
+ Project maintainers who do not follow or enforce the Code of Conduct in good
66
+ faith may face temporary or permanent repercussions as determined by other
67
+ members of the project's leadership.
68
+
69
+ ## Attribution
70
+
71
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
72
+ version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
73
+
74
+ [homepage]: http://contributor-covenant.org
75
+ [version]: http://contributor-covenant.org/version/1/4/
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,32 @@
1
+ # Contributing to MistralClient
2
+
3
+ We welcome bug reports and pull requests from everyone!
4
+
5
+ This project is intended to be a safe, welcoming space for collaboration, and
6
+ contributors are expected to adhere to
7
+ the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
8
+
9
+
10
+ ## Getting Started
11
+
12
+ 1. Fork it ( https://github.com/civisanalytics/mistral_client/fork )
13
+ 2. Install the development dependencies (`bundle install`)
14
+ 3. Make sure you are able to run the test suite locally (`rake`)
15
+ 4. Create a feature branch (`git checkout -b my-new-feature`)
16
+ 5. Make your change. Don't forget tests
17
+ 6. Make sure the test suite, including your new tests, passes (`rake`)
18
+ 7. Commit your changes (`git commit -am 'Add some feature'`)
19
+ 8. Push to the branch (`git push origin my-new-feature`)
20
+ 9. Create a new pull request
21
+ 10. If the Travis build fails, address any issues
22
+
23
+ ## Tips
24
+
25
+ - All pull requests must include test coverage. If you’re not sure how to test
26
+ your changes, feel free to ask for help.
27
+ - Contributions must conform to the
28
+ [Ruby Style Guide](https://github.com/bbatsov/ruby-style-guide).
29
+ - Don’t forget to add your change to the [CHANGELOG](CHANGELOG.md). See
30
+ [Keep a CHANGELOG](http://keepachangelog.com/) for guidelines.
31
+
32
+ Thank you for taking the time to contribute to MistralClient!
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in mistral_client.gemspec
4
+ gemspec
data/LICENSE.md ADDED
@@ -0,0 +1,27 @@
1
+ Copyright (c) 2017, Civis Analytics
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without modification,
5
+ are permitted provided that the following conditions are met:
6
+
7
+ * Redistributions of source code must retain the above copyright notice, this
8
+ list of conditions and the following disclaimer.
9
+
10
+ * Redistributions in binary form must reproduce the above copyright notice, this
11
+ list of conditions and the following disclaimer in the documentation and/or
12
+ other materials provided with the distribution.
13
+
14
+ * Neither the name of Civis Analytics nor the names of its contributors may be
15
+ used to endorse or promote products derived from this software without
16
+ specific prior written permission.
17
+
18
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
22
+ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.md ADDED
@@ -0,0 +1,155 @@
1
+ # MistralClient
2
+
3
+ [![Build Status](https://travis-ci.org/civisanalytics/mistral_client.svg?branch=master)](https://travis-ci.org/civisanalytics/mistral_client)
4
+ [![Gem Version](https://badge.fury.io/rb/mistral_client.svg)](http://badge.fury.io/rb/mistral_client)
5
+ [![Dependency Status](https://gemnasium.com/civisanalytics/mistral_client.svg)](https://gemnasium.com/civisanalytics/mistral_client)
6
+
7
+ MistralClient provides a Ruby interface to a limited subset of
8
+ the [Mistral] [API].
9
+
10
+ [Mistral]: https://wiki.openstack.org/wiki/Mistral
11
+ [API]: https://docs.openstack.org/mistral/latest/api/v2.html
12
+
13
+ ## Installation
14
+
15
+ Add this line to your application's Gemfile:
16
+
17
+ ```ruby
18
+ gem 'mistral_client'
19
+ ```
20
+
21
+ And then execute:
22
+
23
+ $ bundle
24
+
25
+ Or install it yourself as:
26
+
27
+ $ gem install mistral_client
28
+
29
+ ## Usage
30
+
31
+ ### Instantiate a Client
32
+
33
+ The Mistral base URL is required:
34
+
35
+ ```ruby
36
+ client = MistralClient.new('https://mistral.local/v2')
37
+ ```
38
+
39
+ If running Mistral with a self-signed certificate (*e.g.*, during development),
40
+ you can bypass certificate verification by specifying `verify: false`:
41
+
42
+ ```ruby
43
+ client = MistralClient.new('https://mistral.local/v2', verify: false)
44
+ ```
45
+
46
+ You can also pass in other connection options in the same way you would with
47
+ [HTTParty](https://github.com/jnunemaker/httparty):
48
+
49
+ ```ruby
50
+ client = MistralClient.new('https://mistral.local/v2', timeout: 20)
51
+ ```
52
+
53
+ ### Environment Operations
54
+
55
+ #### Create an Environment
56
+
57
+ ```ruby
58
+ environment = client.environment(definition)
59
+ ```
60
+
61
+ #### Delete an Environment
62
+
63
+ ```ruby
64
+ environment.delete!
65
+ ```
66
+
67
+ ### Workflow Operations
68
+
69
+ #### Instantiate a Workflow
70
+
71
+ ```ruby
72
+ workflow = client.workflow(definition)
73
+ ```
74
+
75
+ You can instantiate a workflow object using an existing Mistral workflow ID:
76
+
77
+ ```ruby
78
+ workflow = client.workflow(id: id)
79
+ ```
80
+
81
+ or workflow name:
82
+
83
+ ```ruby
84
+ workflow = client.workflow(name: name)
85
+ ```
86
+
87
+ #### Execute a Workflow
88
+
89
+ ```ruby
90
+ workflow.execute!
91
+ ```
92
+
93
+ If your workflow uses an environment, you can specify the environment with the
94
+ `env` keyword argument:
95
+
96
+ ```ruby
97
+ workflow.execute!(env: name)
98
+ ```
99
+
100
+ #### Update a Workflow Execution
101
+
102
+ ```ruby
103
+ execution.patch(state: 'CANCELLED')
104
+ ```
105
+
106
+ #### Update a Task
107
+
108
+ ```ruby
109
+ task.patch(state: 'RUNNING', reset: true)
110
+ ```
111
+
112
+ #### Delete a Workflow
113
+
114
+ ```ruby
115
+ workflow.delete!
116
+ ```
117
+
118
+ #### Validate a Workflow
119
+
120
+ ```ruby
121
+ client.workflow.valid?(definition)
122
+ ```
123
+
124
+ You can also get validation error details:
125
+
126
+ ```ruby
127
+ client.workflow.validate(definition)
128
+ ```
129
+
130
+ ## Development
131
+
132
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run
133
+ `rake` to run the tests. You can also run `bin/console` for an interactive
134
+ prompt that will allow you to experiment.
135
+
136
+ To install this gem onto your local machine, run `bundle exec rake install`. To
137
+ release a new version, update the version number in `version.rb`, and then run
138
+ `bundle exec rake release`, which will create a git tag for the version, push
139
+ git commits and tags, and push the `.gem` file
140
+ to [rubygems.org](https://rubygems.org).
141
+
142
+ ### To Record New VCR Cassettes
143
+
144
+ Recording new cassettes is as simple as ensuring you have a Mistral server
145
+ available at the `LOCAL_MISTRAL_URL` defined in `spec/spec_helper.rb` and
146
+ running `rake`. To rerecord existing cassettes, simply remove the relevant
147
+ cassettes from `spec/cassettes` and run `rake`.
148
+
149
+ ## Contributing
150
+
151
+ See [CONTRIBUTING](CONTRIBUTING.md).
152
+
153
+ ## License
154
+
155
+ MistralClient is released under the [BSD 3-Clause License](LICENSE.md).
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ require 'rspec/core/rake_task'
4
+ RSpec::Core::RakeTask.new
5
+
6
+ require 'rubocop/rake_task'
7
+ RuboCop::RakeTask.new
8
+
9
+ task default: %i[rubocop spec]
data/bin/console ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'mistral_client'
5
+
6
+ require 'pry'
7
+ Pry.start
data/bin/setup ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
@@ -0,0 +1,37 @@
1
+ module MistralClient
2
+ class ActionExecution < Base
3
+ UNICODE_FIELDS = %w[
4
+ description
5
+ name
6
+ state
7
+ state_info
8
+ task_execution_id
9
+ task_name
10
+ workflow_name
11
+ ].freeze
12
+
13
+ JSON_FIELDS = %w[input output params].freeze
14
+ BOOL_FIELDS = %w[accepted].freeze
15
+
16
+ PATH = 'action_executions'.freeze
17
+
18
+ include MistralClient::Mixins::MistralObject
19
+
20
+ def initialize(server, id: nil)
21
+ @server = server
22
+ @path = 'action_executions'
23
+ @id = id
24
+ reload if @id
25
+ end
26
+
27
+ def patch(state: nil, output: nil)
28
+ body = {}
29
+ body[:state] = state unless state.nil?
30
+ body[:output] = output unless output.nil?
31
+ return if body.empty?
32
+
33
+ resp = @server.put("#{PATH}/#{@id}", body.to_json, json: true)
34
+ ivars_from_response(resp)
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,5 @@
1
+ module MistralClient
2
+ class Base
3
+ DATE_FIELDS = %w[created_at updated_at].freeze
4
+ end
5
+ end
@@ -0,0 +1,76 @@
1
+ module MistralClient
2
+ class Client
3
+ DEFAULT_HTTP_OPTIONS = { verify: true }.freeze
4
+
5
+ def initialize(base, options = {})
6
+ raise ConfigurationError, 'base is required' if base.to_s.empty?
7
+
8
+ @base = base
9
+ @http_options = DEFAULT_HTTP_OPTIONS.merge(options)
10
+ end
11
+
12
+ def delete(path)
13
+ HTTParty.delete("#{@base}/#{path}", @http_options)
14
+ end
15
+
16
+ def get(path)
17
+ resp = HTTParty.get("#{@base}/#{path}", @http_options)
18
+ check_for_error(resp)
19
+ JSON.parse(resp.body)
20
+ end
21
+
22
+ def post(path, body, json: false)
23
+ post_or_put(:post, path, body, json)
24
+ end
25
+
26
+ def put(path, body, json: false)
27
+ post_or_put(:put, path, body, json)
28
+ end
29
+
30
+ def self.resources
31
+ {
32
+ action_execution: MistralClient::ActionExecution,
33
+ environment: MistralClient::Environment,
34
+ execution: MistralClient::Execution,
35
+ task: MistralClient::Task,
36
+ workflow: MistralClient::Workflow
37
+ }
38
+ end
39
+
40
+ def method_missing(name, *args, &block)
41
+ if self.class.resources.keys.include?(name)
42
+ self.class.resources[name].new(self, *args)
43
+ else
44
+ super
45
+ end
46
+ end
47
+
48
+ def respond_to_missing?(name, include_private = false)
49
+ self.class.resources.keys.include?(name) || super
50
+ end
51
+
52
+ private
53
+
54
+ def post_or_put(verb, path, body, json)
55
+ raise ArgumentError unless %i[post put].include?(verb)
56
+ headers = if json
57
+ { 'Content-Type' => 'application/json' }
58
+ else
59
+ { 'Content-Type' => 'text/plain' }
60
+ end
61
+ options = @http_options.merge(headers: headers, body: body)
62
+ resp = HTTParty.send(verb, "#{@base}/#{path}", options)
63
+ check_for_error(resp)
64
+ JSON.parse(resp.body)
65
+ end
66
+
67
+ def check_for_error(resp)
68
+ return if resp.code >= 200 && resp.code < 300
69
+ if resp.code == 404
70
+ raise MissingObjectError, JSON.parse(resp.body)['faultstring']
71
+ end
72
+ raise MistralError,
73
+ "Could not perform the requested operation:\n#{resp.body}"
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,46 @@
1
+ module MistralClient
2
+ class Environment < Base
3
+ UNICODE_FIELDS = %w[name scope description].freeze
4
+ JSON_FIELDS = %w[variables].freeze
5
+ BOOL_FIELDS = [].freeze
6
+ PATH = 'environments'.freeze
7
+ include MistralClient::Mixins::MistralObject
8
+ include MistralClient::Mixins::Definable
9
+
10
+ def initialize(server, definition = nil, name: nil)
11
+ @server = server
12
+ @definition = parse_definition(definition) if definition
13
+ @name = name
14
+ if @name
15
+ reload
16
+ elsif @definition
17
+ create_environment
18
+ end
19
+ end
20
+
21
+ def reload
22
+ super(@name)
23
+ end
24
+
25
+ def delete!
26
+ resp = @server.delete("#{PATH}/#{@name}")
27
+ return true if resp.code == 204
28
+ raise MistralClient::MistralError,
29
+ "Could not perform the requested operation:\n#{resp.body}"
30
+ end
31
+
32
+ private
33
+
34
+ def create_environment
35
+ resp = @server.post(PATH, @definition.to_json, json: true)
36
+ ivars_from_response(resp)
37
+ end
38
+
39
+ def massage_definition(definition)
40
+ if definition['variables'].is_a? Hash
41
+ definition['variables'] = definition['variables'].to_json
42
+ end
43
+ definition
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,6 @@
1
+ module MistralClient
2
+ class MistralError < StandardError; end
3
+
4
+ class ConfigurationError < MistralError; end
5
+ class MissingObjectError < MistralError; end
6
+ end
@@ -0,0 +1,64 @@
1
+ module MistralClient
2
+ class Execution < Base
3
+ UNICODE_FIELDS = %w[
4
+ workflow_id
5
+ workflow_name
6
+ description
7
+ state
8
+ state_info
9
+ ].freeze
10
+
11
+ DATE_FIELDS = %w[created_at updated_at].freeze
12
+ JSON_FIELDS = %w[input output params].freeze
13
+ BOOL_FIELDS = [].freeze
14
+
15
+ PATH = 'executions'.freeze
16
+
17
+ include MistralClient::Mixins::MistralObject
18
+ include MistralClient::Mixins::Definable
19
+ include MistralClient::Mixins::Deletable
20
+
21
+ def initialize(server, workflow_id: nil, env: nil, task_name: nil,
22
+ id: nil)
23
+ set_attributes(server, workflow_id, env, task_name, id)
24
+ if @id
25
+ reload
26
+ elsif @workflow_id
27
+ create_execution
28
+ end
29
+ end
30
+
31
+ def patch(description: nil, state: nil, env: nil)
32
+ body = {}
33
+ body[:description] = description unless description.nil?
34
+ body[:state] = state if state
35
+ body[:params] = { env: env } if env
36
+
37
+ return if body.empty?
38
+
39
+ resp = @server.put("#{PATH}/#{@id}", body.to_json, json: true)
40
+ ivars_from_response(resp)
41
+ end
42
+
43
+ private
44
+
45
+ def set_attributes(server, workflow_id, env, task_name, id)
46
+ @server = server
47
+ @env = env
48
+ @task_name = task_name
49
+ @id = id
50
+ @workflow_id = workflow_id
51
+ end
52
+
53
+ def create_execution
54
+ body = { workflow_id: @workflow_id }
55
+ params = {}
56
+ params[:env] = @env if @env
57
+ params[:task_name] = @task_name if @task_name
58
+ body[:params] = params unless params.empty?
59
+
60
+ resp = @server.post(PATH, body.to_json, json: true)
61
+ ivars_from_response(resp)
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,24 @@
1
+ module MistralClient
2
+ module Mixins
3
+ module Definable
4
+ # rubocop:disable Metrics/MethodLength
5
+ def parse_definition(definition)
6
+ if definition.is_a?(Hash) || definition.is_a?(Array)
7
+ return YAML.dump(definition)
8
+ end
9
+ definition = File.read(definition) if File.exist?(definition)
10
+ # Called outside the if/else to validate the YAML.
11
+ parsed = YAML.safe_load(definition, [], [], true)
12
+ if defined? massage_definition
13
+ massage_definition(parsed)
14
+ else
15
+ definition
16
+ end
17
+ rescue Psych::SyntaxError
18
+ raise ConfigurationError,
19
+ 'Only filenames or raw or parsed strings of YAML are supported.'
20
+ end
21
+ # rubocop:enable Metrics/MethodLength
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,12 @@
1
+ module MistralClient
2
+ module Mixins
3
+ module Deletable
4
+ def delete!
5
+ resp = @server.delete("#{self.class::PATH}/#{id}")
6
+ return true if resp.code == 204
7
+ raise MistralClient::MistralError,
8
+ "Could not perform the requested operation:\n#{resp.body}"
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,52 @@
1
+ module MistralClient
2
+ module Mixins
3
+ module MistralObject
4
+ def self.included(child)
5
+ child.send(:attr_reader, :id)
6
+
7
+ %w[UNICODE_FIELDS DATE_FIELDS JSON_FIELDS BOOL_FIELDS].each do |fields|
8
+ next unless child.const_defined? fields
9
+ child.send(:attr_reader, *child.const_get(fields))
10
+ end
11
+ end
12
+
13
+ def reload(id = @id)
14
+ resp = @server.get("#{self.class::PATH}/#{id}")
15
+ ivars_from_response(resp)
16
+ self
17
+ end
18
+
19
+ def list(params = {})
20
+ resp = @server.get("#{self.class::PATH}?#{URI.encode_www_form(params)}")
21
+ resp[self.class::PATH].map do |t|
22
+ task = self.class.new(@server)
23
+ task.ivars_from_response(t)
24
+ task
25
+ end
26
+ end
27
+
28
+ # rubocop:disable Metrics/AbcSize
29
+ # rubocop:disable Metrics/MethodLength
30
+ def ivars_from_response(resp)
31
+ klass = self.class
32
+ @id = resp['id']
33
+ klass::UNICODE_FIELDS.each do |var|
34
+ instance_variable_set("@#{var}", resp[var]) if resp[var]
35
+ end
36
+ klass::BOOL_FIELDS.each do |var|
37
+ instance_variable_set("@#{var}", resp[var]) if resp.key? var
38
+ end
39
+ klass::DATE_FIELDS.each do |var|
40
+ if resp[var]
41
+ instance_variable_set("@#{var}", DateTime.parse(resp[var]))
42
+ end
43
+ end
44
+ klass::JSON_FIELDS.each do |var|
45
+ instance_variable_set("@#{var}", JSON.parse(resp[var])) if resp[var]
46
+ end
47
+ end
48
+ # rubocop:enable Metrics/MethodLength
49
+ # rubocop:enable Metrics/AbcSize
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,41 @@
1
+ require 'uri'
2
+
3
+ module MistralClient
4
+ class Task < Base
5
+ UNICODE_FIELDS = %w[
6
+ workflow_id
7
+ workflow_execution_id
8
+ workflow_name
9
+ state
10
+ state_info
11
+ result
12
+ name
13
+ ].freeze
14
+
15
+ JSON_FIELDS = %w[published runtime_context].freeze
16
+ BOOL_FIELDS = %w[reset processed].freeze
17
+
18
+ PATH = 'tasks'.freeze
19
+
20
+ include MistralClient::Mixins::MistralObject
21
+
22
+ def initialize(server, id: nil)
23
+ @server = server
24
+ @path = 'tasks'
25
+ @id = id
26
+ reload if id
27
+ end
28
+
29
+ def patch(state: nil, reset: nil, env: nil)
30
+ body = {}
31
+ body[:state] = state if state
32
+ body[:reset] = reset if reset
33
+ body[:env] = env if env
34
+
35
+ return if body.empty?
36
+
37
+ resp = @server.put("#{PATH}/#{@id}", body.to_json, json: true)
38
+ ivars_from_response(resp)
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,3 @@
1
+ module MistralClient
2
+ VERSION = '1.0.0'.freeze
3
+ end
@@ -0,0 +1,53 @@
1
+ module MistralClient
2
+ class Workflow < Base
3
+ UNICODE_FIELDS = %w[definition input name project_id scope].freeze
4
+ BOOL_FIELDS = [].freeze
5
+ JSON_FIELDS = [].freeze
6
+
7
+ PATH = 'workflows'.freeze
8
+
9
+ include MistralClient::Mixins::MistralObject
10
+ include MistralClient::Mixins::Definable
11
+ include MistralClient::Mixins::Deletable
12
+
13
+ def initialize(server, definition = nil, id: nil, name: nil)
14
+ @server = server
15
+ if definition
16
+ @definition = parse_definition(definition)
17
+ create_workflow
18
+ elsif id || name
19
+ @id = id || name
20
+ reload
21
+ end
22
+ end
23
+
24
+ def execute!(env: nil, task_name: nil)
25
+ Execution.new(@server, workflow_id: @id, env: env, task_name: task_name)
26
+ end
27
+
28
+ def valid?(definition)
29
+ resp = perform_validation(definition)
30
+ resp['valid']
31
+ end
32
+
33
+ def validate(definition)
34
+ resp = perform_validation(definition)
35
+ resp['error']&.split("\n")&.first
36
+ end
37
+
38
+ private
39
+
40
+ def create_workflow
41
+ resp = @server.post(PATH, @definition)
42
+ if resp['workflows'].length > 1
43
+ raise ConfigurationError,
44
+ 'Only one workflow per definition is supported'
45
+ end
46
+ ivars_from_response(resp['workflows'][0])
47
+ end
48
+
49
+ def perform_validation(definition)
50
+ @server.post("#{PATH}/validate", parse_definition(definition))
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,22 @@
1
+ require 'httparty'
2
+ require 'yaml'
3
+ require 'mistral_client/mixins/definable'
4
+ require 'mistral_client/mixins/deletable'
5
+ require 'mistral_client/mixins/mistral_object'
6
+ require 'mistral_client/base'
7
+ require 'mistral_client/action_execution'
8
+ require 'mistral_client/client'
9
+ require 'mistral_client/environment'
10
+ require 'mistral_client/error'
11
+ require 'mistral_client/execution'
12
+ require 'mistral_client/task'
13
+ require 'mistral_client/version'
14
+ require 'mistral_client/workflow'
15
+
16
+ module MistralClient
17
+ class << self
18
+ def new(base = nil, options = {})
19
+ Client.new(base, options)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,33 @@
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'mistral_client/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'mistral_client'
7
+ spec.version = MistralClient::VERSION
8
+ spec.authors = ['Matt Brennan', 'Jeff Cousens', 'Mike Saelim']
9
+ spec.email = ['opensource@civisanalytics.com']
10
+
11
+ spec.summary = 'Ruby client for Mistral.'
12
+ spec.description = "A Ruby client for OpenStack's Mistral " \
13
+ '<https://wiki.openstack.org/wiki/Mistral>.'
14
+ spec.homepage = 'https://github.com/civisanalytics/mistral_client'
15
+ spec.license = 'BSD 3-Clause'
16
+
17
+ spec.required_ruby_version = '~> 2.3'
18
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
19
+ f.match(%r{^(test|spec|features)/})
20
+ end
21
+ spec.bindir = 'exe'
22
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
+ spec.require_paths = ['lib']
24
+
25
+ spec.add_dependency 'httparty', '~> 0.15.6'
26
+ spec.add_development_dependency 'bundler', '~> 1.15'
27
+ spec.add_development_dependency 'pry', '~> 0.11.1'
28
+ spec.add_development_dependency 'rake', '~> 12.1'
29
+ spec.add_development_dependency 'rspec', '~> 3.6'
30
+ spec.add_development_dependency 'rubocop', '~> 0.50.0'
31
+ spec.add_development_dependency 'vcr', '~> 3.0'
32
+ spec.add_development_dependency 'webmock', '~> 3.1'
33
+ end
metadata ADDED
@@ -0,0 +1,186 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mistral_client
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Matt Brennan
8
+ - Jeff Cousens
9
+ - Mike Saelim
10
+ autorequire:
11
+ bindir: exe
12
+ cert_chain: []
13
+ date: 2017-10-13 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: httparty
17
+ requirement: !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - "~>"
20
+ - !ruby/object:Gem::Version
21
+ version: 0.15.6
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - "~>"
27
+ - !ruby/object:Gem::Version
28
+ version: 0.15.6
29
+ - !ruby/object:Gem::Dependency
30
+ name: bundler
31
+ requirement: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - "~>"
34
+ - !ruby/object:Gem::Version
35
+ version: '1.15'
36
+ type: :development
37
+ prerelease: false
38
+ version_requirements: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - "~>"
41
+ - !ruby/object:Gem::Version
42
+ version: '1.15'
43
+ - !ruby/object:Gem::Dependency
44
+ name: pry
45
+ requirement: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: 0.11.1
50
+ type: :development
51
+ prerelease: false
52
+ version_requirements: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - "~>"
55
+ - !ruby/object:Gem::Version
56
+ version: 0.11.1
57
+ - !ruby/object:Gem::Dependency
58
+ name: rake
59
+ requirement: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - "~>"
62
+ - !ruby/object:Gem::Version
63
+ version: '12.1'
64
+ type: :development
65
+ prerelease: false
66
+ version_requirements: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - "~>"
69
+ - !ruby/object:Gem::Version
70
+ version: '12.1'
71
+ - !ruby/object:Gem::Dependency
72
+ name: rspec
73
+ requirement: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - "~>"
76
+ - !ruby/object:Gem::Version
77
+ version: '3.6'
78
+ type: :development
79
+ prerelease: false
80
+ version_requirements: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - "~>"
83
+ - !ruby/object:Gem::Version
84
+ version: '3.6'
85
+ - !ruby/object:Gem::Dependency
86
+ name: rubocop
87
+ requirement: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - "~>"
90
+ - !ruby/object:Gem::Version
91
+ version: 0.50.0
92
+ type: :development
93
+ prerelease: false
94
+ version_requirements: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - "~>"
97
+ - !ruby/object:Gem::Version
98
+ version: 0.50.0
99
+ - !ruby/object:Gem::Dependency
100
+ name: vcr
101
+ requirement: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - "~>"
104
+ - !ruby/object:Gem::Version
105
+ version: '3.0'
106
+ type: :development
107
+ prerelease: false
108
+ version_requirements: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - "~>"
111
+ - !ruby/object:Gem::Version
112
+ version: '3.0'
113
+ - !ruby/object:Gem::Dependency
114
+ name: webmock
115
+ requirement: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - "~>"
118
+ - !ruby/object:Gem::Version
119
+ version: '3.1'
120
+ type: :development
121
+ prerelease: false
122
+ version_requirements: !ruby/object:Gem::Requirement
123
+ requirements:
124
+ - - "~>"
125
+ - !ruby/object:Gem::Version
126
+ version: '3.1'
127
+ description: A Ruby client for OpenStack's Mistral <https://wiki.openstack.org/wiki/Mistral>.
128
+ email:
129
+ - opensource@civisanalytics.com
130
+ executables: []
131
+ extensions: []
132
+ extra_rdoc_files: []
133
+ files:
134
+ - ".gitignore"
135
+ - ".rspec"
136
+ - ".rubocop.yml"
137
+ - ".ruby-version"
138
+ - ".travis.yml"
139
+ - CHANGELOG.md
140
+ - CODE_OF_CONDUCT.md
141
+ - CONTRIBUTING.md
142
+ - Gemfile
143
+ - LICENSE.md
144
+ - README.md
145
+ - Rakefile
146
+ - bin/console
147
+ - bin/setup
148
+ - lib/mistral_client.rb
149
+ - lib/mistral_client/action_execution.rb
150
+ - lib/mistral_client/base.rb
151
+ - lib/mistral_client/client.rb
152
+ - lib/mistral_client/environment.rb
153
+ - lib/mistral_client/error.rb
154
+ - lib/mistral_client/execution.rb
155
+ - lib/mistral_client/mixins/definable.rb
156
+ - lib/mistral_client/mixins/deletable.rb
157
+ - lib/mistral_client/mixins/mistral_object.rb
158
+ - lib/mistral_client/task.rb
159
+ - lib/mistral_client/version.rb
160
+ - lib/mistral_client/workflow.rb
161
+ - mistral_client.gemspec
162
+ homepage: https://github.com/civisanalytics/mistral_client
163
+ licenses:
164
+ - BSD 3-Clause
165
+ metadata: {}
166
+ post_install_message:
167
+ rdoc_options: []
168
+ require_paths:
169
+ - lib
170
+ required_ruby_version: !ruby/object:Gem::Requirement
171
+ requirements:
172
+ - - "~>"
173
+ - !ruby/object:Gem::Version
174
+ version: '2.3'
175
+ required_rubygems_version: !ruby/object:Gem::Requirement
176
+ requirements:
177
+ - - ">="
178
+ - !ruby/object:Gem::Version
179
+ version: '0'
180
+ requirements: []
181
+ rubyforge_project:
182
+ rubygems_version: 2.6.13
183
+ signing_key:
184
+ specification_version: 4
185
+ summary: Ruby client for Mistral.
186
+ test_files: []