knife-push 0.5.2 → 1.0.0.pre

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e2e3fa58643a7d957722e6b41f35d385026978e5
4
- data.tar.gz: d23116a303eb6a6b7ec9f6829f1f6bcbd9de2c0a
3
+ metadata.gz: 2dc8400554d194b41e9b0a7d76d5b2754617725e
4
+ data.tar.gz: af456e39586721e50d9379e89e68b5eb0d906e97
5
5
  SHA512:
6
- metadata.gz: 151042bedd64c58c71b5e750c42f70a67c31cb0aa331d71d24c080496d2424b1dee2686274afe828d20553991a846253bbb11cf0fcdb231fe8b52e253ead0a78
7
- data.tar.gz: a0e4fa73d2993a7834d27583d0684d9e143a2c3e7ac00a07b35444d5c13608fad848bb4b027c5697d3d0af852531cda66ef98ff1e491b1ec0f0eb9fc36e4f7df
6
+ metadata.gz: 3d312ddaa30d594c0933f49455dd3cbffeade09b2b9ce33e700d31973d7e71825f7183f140ddfb1305cd1200fd1a1cbdbea466d0f8f6cb2a2025aead4a096700
7
+ data.tar.gz: 342d3aba428326497b981bf09541ab66f1d348157b7301866d012f5dc731491f49a510182db0b1f38fe6b10b876e709410c3d17dae5c09e00e5bedba24705d01
@@ -22,17 +22,17 @@ This plugin provides the following Knife subcommands. Specific command options
22
22
 
23
23
  The <tt>job list</tt> subcommand is used to view a list of Push jobs.
24
24
 
25
- == Syntax
25
+ === Syntax
26
26
  $ knife job list
27
27
 
28
28
  == job start
29
29
 
30
30
  The <tt>job start</tt> subcommand is used to start a Push job.
31
31
 
32
- == Syntax
32
+ === Syntax
33
33
  $ knife job start (options) COMMAND [NODE, NODE, ...]
34
34
 
35
- == Options
35
+ === Options
36
36
  This argument has the following options:
37
37
 
38
38
  --timeout TIMEOUT
@@ -44,11 +44,30 @@ The maximum amount of time (in seconds) by which a job must complete, before it
44
44
  The minimum number of nodes that match the search criteria, are available, and acknowledge the job request. This can be expressed as a
45
45
  percentage (e.g. 50%) or as an absolute number of nodes (e.g. 145). Default value: 100%
46
46
 
47
- -b --no-wait
47
+ -b --nowait
48
48
 
49
49
  Exit immediately after starting a job instead of waiting for it to complete.
50
50
 
51
- == Examples
51
+ --with-env ENVIRONMENT
52
+
53
+ Accept a json blob of environment variables and use those to set the
54
+ variables for the client. For example '{"test": "foo"}' will set the
55
+ push client environment variable "test" to "foo". (Push 2.0 and later)
56
+
57
+ --in-dir DIR
58
+
59
+ Execute the remote command in the directory DIR. (Push 2.0 and later)
60
+
61
+ --file DATAFILE
62
+
63
+ Send the file to the client. (Push 2.0 and later)
64
+
65
+ --capture
66
+
67
+ Capture stdin and stdout for this job. (Push 2.0 and later)
68
+
69
+
70
+ === Examples
52
71
  For example, to search for nodes assigned the role “webapp”, and where 90% of those nodes must be available, enter:
53
72
 
54
73
  $ knife job start -quorum 90% 'chef-client' --search 'role:webapp'
@@ -65,14 +84,35 @@ For example, to run a job named add-glasses against a node named “ricardosalaz
65
84
 
66
85
  $ knife job start add-glasses 'ricardosalazar'
67
86
 
87
+ == job output
88
+
89
+ The <tt>job output </tt> command is used to view the output of Push
90
+ jobs. (Push 2.0 and later). The output capture flag must have been set
91
+ on job start; see the --capture option.
92
+
93
+ === Syntax
94
+
95
+ $ knife job output JOBID
96
+
97
+ === Examples
98
+
99
+ $ knife job output 26e98ba162fa7ba6fb2793125553c7ae test --channel stdout
100
+
101
+ === Options
102
+
103
+ --channel [stderr|stdout]
104
+
105
+ The output channel to capture.
106
+
107
+
68
108
  == job status
69
109
 
70
- The <tt>job status</tt> argument is used to view the status of Push jobs.
110
+ The <tt>job status</tt> command is used to view the status of Push jobs.
71
111
 
72
- == Syntax
73
- $ knife job status
112
+ === Syntax
113
+ $ knife job status JOBID
74
114
 
75
- == Examples
115
+ === Examples
76
116
  For example, to view the status of a job that has the identifier of “235”, enter:
77
117
 
78
118
  $ knife job status 235
@@ -81,10 +121,12 @@ For example, to view the status of a job that has the identifier of “235”, e
81
121
 
82
122
  The <tt>node status</tt> argument is used to identify nodes that Push may interact with.
83
123
 
84
- == Syntax
124
+ === Syntax
85
125
  $ knife node status
86
126
 
87
127
 
128
+
129
+
88
130
  == License
89
131
 
90
132
  Push - The push jobs component for chef
@@ -0,0 +1,141 @@
1
+ # @copyright Copyright 2015 Chef Software, Inc. All Rights Reserved.
2
+ #
3
+ # This file is provided to you under the Apache License,
4
+ # Version 2.0 (the "License"); you may not use this file
5
+ # except in compliance with the License. You may obtain
6
+ # a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing,
11
+ # software distributed under the License is distributed on an
12
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
13
+ # KIND, either express or implied. See the License for the
14
+ # specific language governing permissions and limitations
15
+ # under the License.
16
+ #
17
+
18
+ class Chef
19
+ class Knife
20
+ module JobHelpers
21
+ def process_search(search, nodes)
22
+ node_names = []
23
+ if search
24
+ q = Chef::Search::Query.new
25
+ escaped_query = URI.escape(search,
26
+ Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
27
+ begin
28
+ nodes = q.search(:node, escaped_query).first
29
+ rescue Net::HTTPServerException => e
30
+ msg Chef::JSONCompat.from_json(e.response.body)['error'].first
31
+ ui.error("knife search failed: #{msg}")
32
+ exit 1
33
+ end
34
+ nodes.each { |node| node_names << node.name }
35
+ else
36
+ node_names = nodes
37
+ end
38
+
39
+ if node_names.empty?
40
+ ui.error "No nodes to run job on. Specify nodes as arguments or use -s to specify a search query."
41
+ exit 1
42
+ end
43
+
44
+ return node_names
45
+ end
46
+
47
+ def status_string(job)
48
+ case job['status']
49
+ when 'new'
50
+ [false, 'Initialized.']
51
+ when 'voting'
52
+ [false, job['status'].capitalize + '.']
53
+ else
54
+ total = job['nodes'].values.inject(0) { |sum,nodes| sum+nodes.length }
55
+ in_progress = job['nodes'].keys.inject(0) { |sum,status|
56
+ nodes = job['nodes'][status]
57
+ sum + (%w(new voting running).include?(status) ? 1 : 0)
58
+ }
59
+ if job['status'] == 'running'
60
+ [false, job['status'].capitalize + " (#{in_progress}/#{total} in progress) ..."]
61
+ else
62
+ [true, job['status'].capitalize + '.']
63
+ end
64
+ end
65
+ end
66
+
67
+ def get_quorum(quorum, total_nodes)
68
+ unless qmatch = /^(\d+)(\%?)$/.match(quorum)
69
+ raise "Invalid Format please enter integer or percent"
70
+ end
71
+
72
+ num = qmatch[1]
73
+
74
+ case qmatch[2]
75
+ when "%" then
76
+ ((num.to_f/100)*total_nodes).ceil
77
+ else
78
+ num.to_i
79
+ end
80
+ end
81
+
82
+ def status_code(job)
83
+ if job['status'] == "complete" && job["nodes"].keys.all? do |key|
84
+ key == "succeeded" || key == "nacked" || key == "unavailable"
85
+ end
86
+ 0
87
+ else
88
+ 1
89
+ end
90
+ end
91
+
92
+ def run_helper(config, job_json)
93
+ job_json['run_timeout'] ||= config[:run_timeout].to_i if config[:run_timeout]
94
+
95
+ result = rest.post_rest('pushy/jobs', job_json)
96
+ job_uri = result['uri']
97
+ puts "Started. Job ID: #{job_uri[-32,32]}"
98
+ exit(0) if config[:nowait]
99
+ previous_state = "Initialized."
100
+ begin
101
+ sleep(config[:poll_interval].to_f)
102
+ putc(".")
103
+ job = rest.get_rest(job_uri)
104
+ finished, state = JobHelpers.status_string(job)
105
+ if state != previous_state
106
+ puts state
107
+ previous_state = state
108
+ end
109
+ end until finished
110
+ job
111
+ end
112
+
113
+ def file_helper(file_name)
114
+ if file_name.nil?
115
+ ui.error "No file specified."
116
+ show_usage
117
+ exit 1
118
+ end
119
+ contents = ""
120
+ if File.exists?(file_name)
121
+ File.open(file_name, "rb") do |file|
122
+ contents = file.read
123
+ end
124
+ else
125
+ ui.error "#{file_name} not found"
126
+ exit 1
127
+ end
128
+ return contents
129
+ end
130
+
131
+ def get_env(config)
132
+ env = {}
133
+ begin
134
+ env = config[:with_env] ? JSON.parse(config[:with_env]) : {}
135
+ rescue Exception => e
136
+ Chef::Log.info("Can't parse environment as JSON")
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end
@@ -21,8 +21,6 @@ class Chef
21
21
  banner "knife job list"
22
22
 
23
23
  def run
24
- rest = Chef::REST.new(Chef::Config[:chef_server_url])
25
-
26
24
  jobs = rest.get_rest("pushy/jobs")
27
25
  output(jobs)
28
26
  end
@@ -30,4 +28,3 @@ class Chef
30
28
  end
31
29
  end
32
30
 
33
-
@@ -0,0 +1,55 @@
1
+ # @copyright Copyright 2014 Chef Software, Inc. All Rights Reserved.
2
+ #
3
+ # This file is provided to you under the Apache License,
4
+ # Version 2.0 (the "License"); you may not use this file
5
+ # except in compliance with the License. You may obtain
6
+ # a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing,
11
+ # software distributed under the License is distributed on an
12
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
13
+ # KIND, either express or implied. See the License for the
14
+ # specific language governing permissions and limitations
15
+ # under the License.
16
+ #
17
+
18
+ class Chef
19
+ class Knife
20
+ class JobOutput < Chef::Knife
21
+ banner "knife job output <job id> <node> [<node> ...]"
22
+
23
+ option :channel,
24
+ :long => '--channel stdout|stderr',
25
+ :default => 'stdout',
26
+ :description => "Which output channel to fetch (default stdout)."
27
+
28
+ def run
29
+ job_id = name_args[0]
30
+ channel = get_channel(config[:channel])
31
+ node = name_args[1]
32
+
33
+ uri = "pushy/jobs/#{job_id}/output/#{node}/#{channel}"
34
+
35
+ job = rest.get_rest(uri, false, {"Accept"=>"application/octet-stream"})
36
+
37
+ output(job)
38
+ end
39
+
40
+ def get_channel(channel)
41
+ channel = channel || "stdout"
42
+ case channel
43
+ when "stdout"
44
+ return channel
45
+ when "stderr"
46
+ return channel
47
+ else
48
+ raise "Invalid Format please enter stdout or stderr"
49
+ end
50
+ end
51
+
52
+ end
53
+ end
54
+ end
55
+
@@ -15,10 +15,14 @@
15
15
  # under the License.
16
16
  #
17
17
 
18
+ require 'chef/knife/job_helpers'
19
+
18
20
  class Chef
19
21
  class Knife
20
22
  class JobStart < Chef::Knife
21
23
 
24
+ include JobHelpers
25
+
22
26
  deps do
23
27
  require 'chef/rest'
24
28
  require 'chef/node'
@@ -43,16 +47,45 @@ class Chef
43
47
  :required => false,
44
48
  :description => 'Solr query for list of job candidates.'
45
49
 
50
+ option :send_file,
51
+ :long => '--file FILE',
52
+ :default => nil,
53
+ :description => 'File to send to job.'
54
+
55
+ option :capture_output,
56
+ :long => '--capture',
57
+ :boolean => true,
58
+ :default => false,
59
+ :description => 'Capture job output.'
60
+
61
+ option :with_env,
62
+ :long => "--with-env ENV",
63
+ :default => nil,
64
+ :description => 'JSON blob of environment variables to set.'
65
+
66
+ option :as_user,
67
+ :long => "--as-user USER",
68
+ :default => nil,
69
+ :description => 'User id to run as.'
70
+
71
+ option :in_dir,
72
+ :long => "--in-dir DIR",
73
+ :default => nil,
74
+ :description => 'Directory to execute the command in.'
75
+
46
76
  option :nowait,
47
- :long => '--no-wait',
48
- :short => '-b',
49
- :boolean => true,
50
- :default => false,
51
- :description => "Rather than waiting for each job to complete, exit immediately after starting the job."
77
+ :long => '--nowait',
78
+ :short => '-b',
79
+ :boolean => true,
80
+ :default => false,
81
+ :description => "Rather than waiting for each job to complete, exit immediately after starting the job."
52
82
 
53
- def run
54
- @node_names = []
83
+ option :poll_interval,
84
+ :long => '--poll-interval RATE',
85
+ :default => 1.0,
86
+ :description => "Repeat interval for job status update (in seconds)."
55
87
 
88
+ def run
56
89
  job_name = @name_args[0]
57
90
  if job_name.nil?
58
91
  ui.error "No job specified."
@@ -60,101 +93,29 @@ class Chef
60
93
  exit 1
61
94
  end
62
95
 
63
- if config[:search]
64
- q = Chef::Search::Query.new
65
- @escaped_query = URI.escape(config[:search],
66
- Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
67
- begin
68
- nodes = q.search(:node, @escaped_query).first
69
- rescue Net::HTTPServerException => e
70
- msg Chef::JSONCompat.from_json(e.response.body)['error'].first
71
- ui.error("knife search failed: #{msg}")
72
- exit 1
73
- end
74
- nodes.each { |node| @node_names << node.name }
75
- else
76
- @node_names = name_args[1,name_args.length-1]
77
- end
78
-
79
- if @node_names.empty?
80
- ui.error "No nodes to run job on. Specify nodes as arguments or use -s to specify a search query."
81
- exit 1
82
- end
83
-
84
- rest = Chef::REST.new(Chef::Config[:chef_server_url])
96
+ @node_names = process_search(config[:search], name_args[1,@name_args.length-1])
85
97
 
86
98
  job_json = {
87
99
  'command' => job_name,
88
100
  'nodes' => @node_names,
89
- 'quorum' => get_quorum(config[:quorum], @node_names.length)
101
+ 'capture_output' => config[:capture_output]
90
102
  }
91
- job_json['run_timeout'] = config[:run_timeout].to_i if config[:run_timeout]
92
- result = rest.post_rest('pushy/jobs', job_json)
93
- job_uri = result['uri']
94
- puts "Started. Job ID: #{job_uri[-32,32]}"
95
- exit(0) if config[:nowait]
96
- previous_state = "Initialized."
97
- begin
98
- sleep(0.1)
99
- job = rest.get_rest(job_uri)
100
- finished, state = status_string(job)
101
- if state != previous_state
102
- puts state
103
- previous_state = state
104
- end
105
- end until finished
103
+ job_json['file'] = "raw:" + file_helper(config[:send_file]) if config[:send_file]
104
+ job_json['quorum'] = get_quorum(config[:quorum], @node_names.length)
105
+ env = get_env(config)
106
+ job_json['env'] = env if env
107
+ job_json['dir'] = config[:in_dir] if config[:in_dir]
108
+ job_json['user'] = config[:as_user] if config[:as_user]
109
+
110
+ job = run_helper(config, job_json)
106
111
 
107
112
  output(job)
108
113
 
109
114
  exit(status_code(job))
110
- end
111
-
112
- private
113
-
114
- def status_string(job)
115
- case job['status']
116
- when 'new'
117
- [false, 'Initialized.']
118
- when 'voting'
119
- [false, job['status'].capitalize + '.']
120
- else
121
- total = job['nodes'].values.inject(0) { |sum,nodes| sum+nodes.length }
122
- in_progress = job['nodes'].keys.inject(0) { |sum,status|
123
- nodes = job['nodes'][status]
124
- sum + (%w(new voting running).include?(status) ? 1 : 0)
125
- }
126
- if job['status'] == 'running'
127
- [false, job['status'].capitalize + " (#{in_progress}/#{total} in progress) ..."]
128
- else
129
- [true, job['status'].capitalize + '.']
130
- end
131
- end
132
- end
133
115
 
134
- def get_quorum(quorum, total_nodes)
135
- unless qmatch = /^(\d+)(\%?)$/.match(quorum)
136
- raise "Invalid Format please enter integer or percent"
137
- end
138
-
139
- num = qmatch[1]
140
-
141
- case qmatch[2]
142
- when "%" then
143
- ((num.to_f/100)*total_nodes).ceil
144
- else
145
- num.to_i
146
- end
147
116
  end
148
117
 
149
- def status_code(job)
150
- if job['status'] == "complete" && job["nodes"].keys.all? do |key|
151
- key == "succeeded" || key == "nacked" || key == "unavailable"
152
- end
153
- 0
154
- else
155
- 1
156
- end
157
- end
118
+ private
158
119
 
159
120
  end
160
121
  end
@@ -14,15 +14,12 @@
14
14
  # specific language governing permissions and limitations
15
15
  # under the License.
16
16
  #
17
-
18
17
  class Chef
19
18
  class Knife
20
19
  class JobStatus < Chef::Knife
21
20
  banner "knife job status <job id>"
22
21
 
23
22
  def run
24
- rest = Chef::REST.new(Chef::Config[:chef_server_url])
25
-
26
23
  job_id = name_args[0]
27
24
  job = rest.get_rest("pushy/jobs/#{job_id}")
28
25
  output(job)
@@ -21,8 +21,6 @@ class Chef
21
21
  banner "knife node status [<node> <node> ...]"
22
22
 
23
23
  def run
24
- rest = Chef::REST.new(Chef::Config[:chef_server_url])
25
-
26
24
  get_node_statuses(name_args).each do |node_status|
27
25
  puts "#{node_status['node_name']}\t#{node_status['availability']}"
28
26
  end
@@ -1,6 +1,7 @@
1
+
1
2
  module Knife
2
3
  module Push
3
- VERSION = '0.5.2'
4
+ VERSION = '1.0.0.pre'
4
5
  MAJOR, MINOR, TINY = VERSION.split('.')
5
6
  end
6
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: knife-push
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.2
4
+ version: 1.0.0.pre
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Keiser
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-09-09 00:00:00.000000000 Z
11
+ date: 2016-03-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: chef
@@ -38,7 +38,7 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
- description: Knife plugin for OPC push
41
+ description: Knife plugin for chef push
42
42
  email: jkeiser@opscode.com
43
43
  executables: []
44
44
  extensions: []
@@ -49,12 +49,14 @@ files:
49
49
  - LICENSE
50
50
  - README.rdoc
51
51
  - Rakefile
52
+ - lib/chef/knife/job_helpers.rb
52
53
  - lib/chef/knife/job_list.rb
54
+ - lib/chef/knife/job_output.rb
53
55
  - lib/chef/knife/job_start.rb
54
56
  - lib/chef/knife/job_status.rb
55
57
  - lib/chef/knife/node_status.rb
56
58
  - lib/knife-push/version.rb
57
- homepage: http://www.opscode.com
59
+ homepage: http://www.chef.io
58
60
  licenses: []
59
61
  metadata: {}
60
62
  post_install_message:
@@ -68,13 +70,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
68
70
  version: '0'
69
71
  required_rubygems_version: !ruby/object:Gem::Requirement
70
72
  requirements:
71
- - - ">="
73
+ - - ">"
72
74
  - !ruby/object:Gem::Version
73
- version: '0'
75
+ version: 1.3.1
74
76
  requirements: []
75
77
  rubyforge_project:
76
- rubygems_version: 2.2.2
78
+ rubygems_version: 2.2.3
77
79
  signing_key:
78
80
  specification_version: 4
79
- summary: Knife plugin for OPC push
81
+ summary: Knife plugin for chef push
80
82
  test_files: []
83
+ has_rdoc: true