knife-push 0.5.2 → 1.0.0.pre

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
  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