knife-ec2 0.16.0 → 0.17.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
  SHA1:
3
- metadata.gz: 0453d28ca1ee7ca9304f301fb2cf3e441cb10831
4
- data.tar.gz: bee091b5395aa6fc75fed85f9810b29ba7547b5e
3
+ metadata.gz: 902fa10935b516871981f149f29fe37890bfd3a3
4
+ data.tar.gz: 84519150709c6e46de97de7cbd1be1100bad8c50
5
5
  SHA512:
6
- metadata.gz: 01ccbc1d97798ae53cf5c6155d3398dc7c5ab0727e99effc608ff624f8b72cfca9b36ffd4e19c48e6d5c5baff263cde0060d490f410d437bace8e145aaf44258
7
- data.tar.gz: 88b79da6a34a5cc3292b202951b327b4bbb6f1124da79ef705a4efe20d64dcddffdc235ccc8cb117ca2fd7dde929bbd7c8186d7608f9d656e56a94dee8a2773a
6
+ metadata.gz: c66211a3338d6ef2ff6c954c33dbb85f71b82faf8e625f0c07bf27f9f73e5d5c599dadf798b56e09ce1ec27ce8e8fd37cb346f78369bd843c7face475325b24a
7
+ data.tar.gz: d0d18e02338ffb8007b6b6ee6c3d8f280adfa2fd1b5c2b22109ea335ae132f9fa47fe0c9eae117fece8b6b86f141a7aa72b0126b36fdb7f0e4cee352f232e8a8
data/CHANGELOG.md CHANGED
@@ -1,109 +1,114 @@
1
- # knife-ec2 change log
2
-
3
- Note: this log contains only changes from knife-ec2 release 0.8.0 and later
4
- -- it does not contain the changes from prior releases. To view change history
5
- prior to release 0.8.0, please visit the [source repository](https://github.com/chef/knife-ec2/commits).
6
-
7
-
8
- ## Latest Release 0.16.0 (2017-11-07)
9
- * [knife-ec2:#503](https://github.com/chef/knife-ec2/pull/503) Update list of instance_types that support ebs-encryption.
10
- * [knife-ec2:#496](https://github.com/chef/knife-ec2/pull/496) Change Winrm cert to 10 year expiry.
11
- * [knife-ec2:#492](https://github.com/chef/knife-ec2/pull/492) Added support to tag node details to chef.
12
- * [knife-ec2:#490](https://github.com/chef/knife-ec2/pull/490) Improper alignment of EC2 flavor list.
13
- * [knife-ec2:#489](https://github.com/chef/knife-ec2/pull/489) Added support to handle long passwords in windows.
14
- * [knife-ec2:#488](https://github.com/chef/knife-ec2/pull/488) Added support to tag EBS volumes on node creation.
15
- * [knife-ec2:#487](https://github.com/chef/knife-ec2/pull/487) Added new column description in EC2 AMIs list.
16
-
17
- ## Latest Release 0.15.0 (2017-02-15)
18
- * [knife-ec2:#484](https://github.com/chef/knife-ec2/pull/484) sleep for collecting windows password
19
- * [knife-ec2:#481](https://github.com/chef/knife-ec2/pull/481) Updated readme for EC2 AMI list
20
- * [knife-ec2:#482](https://github.com/chef/knife-ec2/pull/482) Allow search for EC2 AMIs
21
- * [knife-ec2:#471](https://github.com/chef/knife-ec2/pull/471) Added support to include ec2 server id in the node name using `-N "www-server-%s" or --chef-node-name "-www-server-%s"`
22
- * [knife-ec2:#478](https://github.com/chef/knife-ec2/pull/478) Allow for hosts without public ip addresses
23
- * [knife-ec2:#476](https://github.com/chef/knife-ec2/pull/476) Tag node in chef
24
- * [knife-ec2:#458](https://github.com/chef/knife-ec2/pull/458) Fix where yes option wasn’t being passed to bootstrap
25
- * [knife-ec2:#468](https://github.com/chef/knife-ec2/pull/468) In VPC mode use private IP when public IP and DNS not available
26
- * [knife-ec2:#464](https://github.com/chef/knife-ec2/pull/464) default value and desription is changed for --ebs-volume-type
27
-
28
- ## Latest Release 0.14.0 (2016-12-02)
29
- * `knife-ec2` requires `chef-client 12.14` or newer.
30
- * [knife-ec2:#442](https://github.com/chef/knife-ec2/pull/442) Added support to show flavor list in json format.
31
- * [knife-ec2:#439](https://github.com/chef/knife-ec2/pull/439) Allow to accept multiple security group ids using --security-group-id option multiple times and added deprecated message for comma seprated --security-group-ids option.
32
-
33
- ## Release 0.13.0 (2016-08-05)
34
- * [knife-ec2:#438](https://github.com/chef/knife-ec2/pull/438) Specs for ec2 server list
35
- * [knife-ec2:#437](https://github.com/chef/knife-ec2/pull/437) Fix --aws-credential-file issues
36
- * [knife-ec2:#436](https://github.com/chef/knife-ec2/pull/436) basic is not a valid setting for winrm_ssl_verify_mode
37
- * [knife-ec2:#435](https://github.com/chef/knife-ec2/pull/435) Fix for Winrm Authentication Failure issue during bootstrap
38
- * [knife-ec2:#434](https://github.com/chef/knife-ec2/pull/434) Removed ruby2.0.0 from travis
39
- * [knife-ec2:#431](https://github.com/chef/knife-ec2/pull/431) Pinned rack and ffi-yajl gem to older versions.
40
- * [knife-ec2:#430](https://github.com/chef/knife-ec2/pull/430) Fixing --aws-config-file issues
41
- * [knife-ec2:#429](https://github.com/chef/knife-ec2/pull/429) Added validation when spot-wait-mode option is given by user on CLI and spot-price option is not given.
42
- * [knife-ec2:#428](https://github.com/chef/knife-ec2/pull/428) Fix for s3 secret not getting copied on target vm
43
- * [knife-ec2:#427](https://github.com/chef/knife-ec2/pull/427) Addedd disable_api_termination option along with RSpecs.
44
- * [knife-ec2:#416](https://github.com/chef/knife-ec2/pull/416) Modified help for option --security-group-ids
45
- * [knife-ec2:#409](https://github.com/chef/knife-ec2/pull/409) Passing encrypted\_data\_bag\_secret and encrypted\_databag\_secret\_file
46
- * [knife-ec2:#405](https://github.com/chef/knife-ec2/pull/405) Updated README file - added description of aws\_config\_file option
47
- * [knife-ec2:#399](https://github.com/chef/knife-ec2/pull/399) Adding support for aws-config-file
48
- * [knife-ec2:#400](https://github.com/chef/knife-ec2/pull/400) Added --json-for-attributes-file
49
- * [knife-ec2:#393](https://github.com/chef/knife-ec2/pull/393) Please also read aws\_session\_token from credentials file - [Richard Morrisey](https://github.com/datascope)
50
- * [knife-ec2:#395](https://github.com/chef/knife-ec2/pull/395) Fix security groups for spot requests in a VPC and make user input optional - [Mikhail Bautin](https://github.com/mbautin)
51
- * [knife-ec2:#322](https://github.com/chef/knife-ec2/pull/322) Implement support for ClassicLink [Quention de Metz](https://github.com/quentindemetz)
52
- * [knife-ec2:#391](https://github.com/chef/knife-ec2/pull/391) adding missing m4,d2,t2,and g2 ebs encryption flavors - [Mario Harvey](https://github.com/badmadrad)
53
- * [knife-ec2:#390](https://github.com/chef/knife-ec2/pull/390) Modified create\_ssl\_listener option as per Mixlib-CLI.
54
- * [knife-ec2:#375](https://github.com/chef/knife-ec2/pull/375) Attach network interfaces before bootstrap - [Eric Herot](https://github.com/eherot)
55
- * [knife-ec2:#389](https://github.com/chef/knife-ec2/pull/389) --server-connect-attribute cleanup
56
- * [knife-ec2:#388](https://github.com/chef/knife-ec2/pull/388) Updated Readme for --server-connect-attribute option
57
- * [knife-ec2:#384](https://github.com/chef/knife-ec2/pull/384) server list in json format
58
- * [knife-ec2:#378](https://github.com/chef/knife-ec2/pull/378) Readme improvements
59
- * [knife-ec2:#376](https://github.com/chef/knife-ec2/pull/376) Remove instance colors
60
- * [knife-ec2:#377](https://github.com/chef/knife-ec2/pull/377) Require fog-aws vs. fog
61
- * [knife-ec2:#368](https://github.com/chef/knife-ec2/pull/368) Handle Errno::ENOTCONN when testing for sshd access - [Eugene Bolshakov](https://github.com/eugenebolshakov)
62
- * [knife-ec2:#373](https://github.com/chef/knife-ec2/pull/373) Update contributing docs
63
- * [knife-ec2:#374](https://github.com/chef/knife-ec2/pull/374) Avoid sending nil runlist to Chef::Knife::Boostrap
64
- * [knife-ec2:#372](https://github.com/chef/knife-ec2/pull/372) Cache gems in travis, update links and opscode -> chef
65
- * [knife-ec2:#371](https://github.com/chef/knife-ec2/pull/371) fix typo in readme - [Kyle West](https://github.com/kylewest)
66
- * [knife-ec2:#363](https://github.com/chef/knife-ec2/pull/363) Add ssl config user data for ssl transport, if required append to user\_data script specified by user.
67
- * [knife-ec2:#319](https://github.com/chef/knife-ec2/pull/319) Pointing docs at itself. This is better then the non-existent chef.io docs.
68
-
69
- ## Release: 0.12.0 (2015-10-1)
70
- * [knife-ec2:#305](https://github.com/chef/knife-ec2/pull/305) Updates to support standard .aws/credentials file
71
- * [knife-ec2 #354](https://github.com/chef/knife-ec2/pull/354) knife-windows 1.0.0 dependency, support for validatorless bootstrap, other Chef 12 bootstrap options
72
- * [knife-ec2 #356](https://github.com/chef/knife-ec2/pull/356) Added --forward-agent option
73
-
74
- ## Release: 0.11.0 (2015-08-24)
75
- * [knife-ec2:#330](https://github.com/chef/knife-ec2/pull/330) Modification for attribute precedence issue
76
- * [knife-ec2:#293](https://github.com/chef/knife-ec2/pull/293) s3_source: Lazy load fog library
77
- * [knife-ec2:#284](https://github.com/chef/knife-ec2/pull/284) Enable Spot Pricing
78
- * [knife-ec2:#280](https://github.com/chef/knife-ec2/pull/280) Support for EBS volume encryption in knife-ec2 server create options
79
- * [knife-ec2:#273](https://github.com/chef/knife-ec2/pull/273) Remove -s option for data bag secret and subnets
80
- * [knife-ec2:#268](https://github.com/chef/knife-ec2/pull/268) Updated gemspec to use fog v1.25
81
- * [knife-ec2:#265](https://github.com/chef/knife-ec2/pull/265) showing error message for incorrect option input
82
- * [knife-ec2:#261](https://github.com/chef/knife-ec2/pull/261) Remove 'em-winrm' gem dependency
83
- * [KNIFE-464](https://tickets.opscode.com/browse/KNIFE-464) Support EC2 STS, i.e. AWS Federation tokens for authentication
84
-
85
- ## Release: 0.10.0.rc.1 (2014-10-08)
86
- * [Issue:#237](https://github.com/chef/knife-ec2/issues/237) Provide a way to the validation key and data bag secret from S3
87
- * [Issue:#243](https://github.com/chef/knife-ec2/issues/243) Support new AWS CLI configuration file format
88
- * Update `knife-windows` gem dependency to `knife-windows 0.8.rc.0` for improved Windows authentication integration
89
- * Update `fog` gem dependency to `fog 1.23.0`
90
- * Provisioned IOPS support via the `--provisioned-iops` and `--ebs-volume-type` options
91
- * [KNIFE-466](https://tickets.opscode.com/browse/KNIFE-466) Knife ec2 should use gateway from net::ssh config if available
92
- * [KNIFE-422](https://tickets.opscode.com/browse/KNIFE-422) Knife ec2 server create doesn't respect identity file of gateway server from ssh\_config
93
-
94
- ## Release: 0.8.0 (2014-03-10)
95
- * [KNIFE-458](https://tickets.opscode.com/browse/KNIFE-458) Docs: Increase detail about necessary
96
- options for VPC instance creation
97
- * [KNIFE-456](https://tickets.opscode.com/browse/KNIFE-456) Documentation for :aws\_credential\_file difficult to read
98
- * [KNIFE-455](https://tickets.opscode.com/browse/KNIFE-455) knife ec2 may try to use private ip for vpc bootstrap even with --associate-public-ip flag
99
- * [KNIFE-453](https://tickets.opscode.com/browse/KNIFE-453) knife-ec2 doesn't handle aws credentials files with windows line endings
100
- * [KNIFE-451](https://tickets.opscode.com/browse/KNIFE-451) Update Fog version to 1.20.0
101
- * [KNIFE-430](https://tickets.opscode.com/browse/KNIFE-430) server creation tunnelling should wait for a valid banner before continuing
102
- * [KNIFE-381](https://tickets.opscode.com/browse/KNIFE-381) Gabriel Rosendorf Add ability to associate public ip with VPC
103
- instance on creation
104
-
105
- ## Releases prior to 0.8.0
106
- Please see <https://github.com/chef/knife-ec2/commits> to view changes in
107
- the form of commits to the source repository for releases before 0.8.0.
108
-
109
-
1
+ # knife-ec2 change log
2
+
3
+ Note: this log contains only changes from knife-ec2 release 0.8.0 and later
4
+ -- it does not contain the changes from prior releases. To view change history
5
+ prior to release 0.8.0, please visit the [source repository](https://github.com/chef/knife-ec2/commits).
6
+
7
+
8
+ ## Latest Release 0.17.0 (2018-02-07)
9
+ * [knife-ec2:#515](https://github.com/chef/knife-ec2/pull/515) Allow re-use of existing ENI for primary interface.
10
+ * [knife-ec2:#514](https://github.com/chef/knife-ec2/pull/514) Add `--instance-initiated-shutdown-behavior` option.
11
+ * [knife-ec2:#513](https://github.com/chef/knife-ec2/pull/513) require `rb-readline` to avoid ruby 2.4 warnings about `Fixnum`.
12
+
13
+ ## Latest Release 0.16.0 (2017-11-07)
14
+ * [knife-ec2:#503](https://github.com/chef/knife-ec2/pull/503) Update list of instance_types that support ebs-encryption.
15
+ * [knife-ec2:#496](https://github.com/chef/knife-ec2/pull/496) Change Winrm cert to 10 year expiry.
16
+ * [knife-ec2:#492](https://github.com/chef/knife-ec2/pull/492) Added support to tag node details to chef.
17
+ * [knife-ec2:#490](https://github.com/chef/knife-ec2/pull/490) Improper alignment of EC2 flavor list.
18
+ * [knife-ec2:#489](https://github.com/chef/knife-ec2/pull/489) Added support to handle long passwords in windows.
19
+ * [knife-ec2:#488](https://github.com/chef/knife-ec2/pull/488) Added support to tag EBS volumes on node creation.
20
+ * [knife-ec2:#487](https://github.com/chef/knife-ec2/pull/487) Added new column description in EC2 AMIs list.
21
+
22
+ ## Latest Release 0.15.0 (2017-02-15)
23
+ * [knife-ec2:#484](https://github.com/chef/knife-ec2/pull/484) sleep for collecting windows password
24
+ * [knife-ec2:#481](https://github.com/chef/knife-ec2/pull/481) Updated readme for EC2 AMI list
25
+ * [knife-ec2:#482](https://github.com/chef/knife-ec2/pull/482) Allow search for EC2 AMIs
26
+ * [knife-ec2:#471](https://github.com/chef/knife-ec2/pull/471) Added support to include ec2 server id in the node name using `-N "www-server-%s" or --chef-node-name "-www-server-%s"`
27
+ * [knife-ec2:#478](https://github.com/chef/knife-ec2/pull/478) Allow for hosts without public ip addresses
28
+ * [knife-ec2:#476](https://github.com/chef/knife-ec2/pull/476) Tag node in chef
29
+ * [knife-ec2:#458](https://github.com/chef/knife-ec2/pull/458) Fix where yes option wasn’t being passed to bootstrap
30
+ * [knife-ec2:#468](https://github.com/chef/knife-ec2/pull/468) In VPC mode use private IP when public IP and DNS not available
31
+ * [knife-ec2:#464](https://github.com/chef/knife-ec2/pull/464) default value and desription is changed for --ebs-volume-type
32
+
33
+ ## Latest Release 0.14.0 (2016-12-02)
34
+ * `knife-ec2` requires `chef-client 12.14` or newer.
35
+ * [knife-ec2:#442](https://github.com/chef/knife-ec2/pull/442) Added support to show flavor list in json format.
36
+ * [knife-ec2:#439](https://github.com/chef/knife-ec2/pull/439) Allow to accept multiple security group ids using --security-group-id option multiple times and added deprecated message for comma seprated --security-group-ids option.
37
+
38
+ ## Release 0.13.0 (2016-08-05)
39
+ * [knife-ec2:#438](https://github.com/chef/knife-ec2/pull/438) Specs for ec2 server list
40
+ * [knife-ec2:#437](https://github.com/chef/knife-ec2/pull/437) Fix --aws-credential-file issues
41
+ * [knife-ec2:#436](https://github.com/chef/knife-ec2/pull/436) basic is not a valid setting for winrm_ssl_verify_mode
42
+ * [knife-ec2:#435](https://github.com/chef/knife-ec2/pull/435) Fix for Winrm Authentication Failure issue during bootstrap
43
+ * [knife-ec2:#434](https://github.com/chef/knife-ec2/pull/434) Removed ruby2.0.0 from travis
44
+ * [knife-ec2:#431](https://github.com/chef/knife-ec2/pull/431) Pinned rack and ffi-yajl gem to older versions.
45
+ * [knife-ec2:#430](https://github.com/chef/knife-ec2/pull/430) Fixing --aws-config-file issues
46
+ * [knife-ec2:#429](https://github.com/chef/knife-ec2/pull/429) Added validation when spot-wait-mode option is given by user on CLI and spot-price option is not given.
47
+ * [knife-ec2:#428](https://github.com/chef/knife-ec2/pull/428) Fix for s3 secret not getting copied on target vm
48
+ * [knife-ec2:#427](https://github.com/chef/knife-ec2/pull/427) Addedd disable_api_termination option along with RSpecs.
49
+ * [knife-ec2:#416](https://github.com/chef/knife-ec2/pull/416) Modified help for option --security-group-ids
50
+ * [knife-ec2:#409](https://github.com/chef/knife-ec2/pull/409) Passing encrypted\_data\_bag\_secret and encrypted\_databag\_secret\_file
51
+ * [knife-ec2:#405](https://github.com/chef/knife-ec2/pull/405) Updated README file - added description of aws\_config\_file option
52
+ * [knife-ec2:#399](https://github.com/chef/knife-ec2/pull/399) Adding support for aws-config-file
53
+ * [knife-ec2:#400](https://github.com/chef/knife-ec2/pull/400) Added --json-for-attributes-file
54
+ * [knife-ec2:#393](https://github.com/chef/knife-ec2/pull/393) Please also read aws\_session\_token from credentials file - [Richard Morrisey](https://github.com/datascope)
55
+ * [knife-ec2:#395](https://github.com/chef/knife-ec2/pull/395) Fix security groups for spot requests in a VPC and make user input optional - [Mikhail Bautin](https://github.com/mbautin)
56
+ * [knife-ec2:#322](https://github.com/chef/knife-ec2/pull/322) Implement support for ClassicLink [Quention de Metz](https://github.com/quentindemetz)
57
+ * [knife-ec2:#391](https://github.com/chef/knife-ec2/pull/391) adding missing m4,d2,t2,and g2 ebs encryption flavors - [Mario Harvey](https://github.com/badmadrad)
58
+ * [knife-ec2:#390](https://github.com/chef/knife-ec2/pull/390) Modified create\_ssl\_listener option as per Mixlib-CLI.
59
+ * [knife-ec2:#375](https://github.com/chef/knife-ec2/pull/375) Attach network interfaces before bootstrap - [Eric Herot](https://github.com/eherot)
60
+ * [knife-ec2:#389](https://github.com/chef/knife-ec2/pull/389) --server-connect-attribute cleanup
61
+ * [knife-ec2:#388](https://github.com/chef/knife-ec2/pull/388) Updated Readme for --server-connect-attribute option
62
+ * [knife-ec2:#384](https://github.com/chef/knife-ec2/pull/384) server list in json format
63
+ * [knife-ec2:#378](https://github.com/chef/knife-ec2/pull/378) Readme improvements
64
+ * [knife-ec2:#376](https://github.com/chef/knife-ec2/pull/376) Remove instance colors
65
+ * [knife-ec2:#377](https://github.com/chef/knife-ec2/pull/377) Require fog-aws vs. fog
66
+ * [knife-ec2:#368](https://github.com/chef/knife-ec2/pull/368) Handle Errno::ENOTCONN when testing for sshd access - [Eugene Bolshakov](https://github.com/eugenebolshakov)
67
+ * [knife-ec2:#373](https://github.com/chef/knife-ec2/pull/373) Update contributing docs
68
+ * [knife-ec2:#374](https://github.com/chef/knife-ec2/pull/374) Avoid sending nil runlist to Chef::Knife::Boostrap
69
+ * [knife-ec2:#372](https://github.com/chef/knife-ec2/pull/372) Cache gems in travis, update links and opscode -> chef
70
+ * [knife-ec2:#371](https://github.com/chef/knife-ec2/pull/371) fix typo in readme - [Kyle West](https://github.com/kylewest)
71
+ * [knife-ec2:#363](https://github.com/chef/knife-ec2/pull/363) Add ssl config user data for ssl transport, if required append to user\_data script specified by user.
72
+ * [knife-ec2:#319](https://github.com/chef/knife-ec2/pull/319) Pointing docs at itself. This is better then the non-existent chef.io docs.
73
+
74
+ ## Release: 0.12.0 (2015-10-1)
75
+ * [knife-ec2:#305](https://github.com/chef/knife-ec2/pull/305) Updates to support standard .aws/credentials file
76
+ * [knife-ec2 #354](https://github.com/chef/knife-ec2/pull/354) knife-windows 1.0.0 dependency, support for validatorless bootstrap, other Chef 12 bootstrap options
77
+ * [knife-ec2 #356](https://github.com/chef/knife-ec2/pull/356) Added --forward-agent option
78
+
79
+ ## Release: 0.11.0 (2015-08-24)
80
+ * [knife-ec2:#330](https://github.com/chef/knife-ec2/pull/330) Modification for attribute precedence issue
81
+ * [knife-ec2:#293](https://github.com/chef/knife-ec2/pull/293) s3_source: Lazy load fog library
82
+ * [knife-ec2:#284](https://github.com/chef/knife-ec2/pull/284) Enable Spot Pricing
83
+ * [knife-ec2:#280](https://github.com/chef/knife-ec2/pull/280) Support for EBS volume encryption in knife-ec2 server create options
84
+ * [knife-ec2:#273](https://github.com/chef/knife-ec2/pull/273) Remove -s option for data bag secret and subnets
85
+ * [knife-ec2:#268](https://github.com/chef/knife-ec2/pull/268) Updated gemspec to use fog v1.25
86
+ * [knife-ec2:#265](https://github.com/chef/knife-ec2/pull/265) showing error message for incorrect option input
87
+ * [knife-ec2:#261](https://github.com/chef/knife-ec2/pull/261) Remove 'em-winrm' gem dependency
88
+ * [KNIFE-464](https://tickets.opscode.com/browse/KNIFE-464) Support EC2 STS, i.e. AWS Federation tokens for authentication
89
+
90
+ ## Release: 0.10.0.rc.1 (2014-10-08)
91
+ * [Issue:#237](https://github.com/chef/knife-ec2/issues/237) Provide a way to the validation key and data bag secret from S3
92
+ * [Issue:#243](https://github.com/chef/knife-ec2/issues/243) Support new AWS CLI configuration file format
93
+ * Update `knife-windows` gem dependency to `knife-windows 0.8.rc.0` for improved Windows authentication integration
94
+ * Update `fog` gem dependency to `fog 1.23.0`
95
+ * Provisioned IOPS support via the `--provisioned-iops` and `--ebs-volume-type` options
96
+ * [KNIFE-466](https://tickets.opscode.com/browse/KNIFE-466) Knife ec2 should use gateway from net::ssh config if available
97
+ * [KNIFE-422](https://tickets.opscode.com/browse/KNIFE-422) Knife ec2 server create doesn't respect identity file of gateway server from ssh\_config
98
+
99
+ ## Release: 0.8.0 (2014-03-10)
100
+ * [KNIFE-458](https://tickets.opscode.com/browse/KNIFE-458) Docs: Increase detail about necessary
101
+ options for VPC instance creation
102
+ * [KNIFE-456](https://tickets.opscode.com/browse/KNIFE-456) Documentation for :aws\_credential\_file difficult to read
103
+ * [KNIFE-455](https://tickets.opscode.com/browse/KNIFE-455) knife ec2 may try to use private ip for vpc bootstrap even with --associate-public-ip flag
104
+ * [KNIFE-453](https://tickets.opscode.com/browse/KNIFE-453) knife-ec2 doesn't handle aws credentials files with windows line endings
105
+ * [KNIFE-451](https://tickets.opscode.com/browse/KNIFE-451) Update Fog version to 1.20.0
106
+ * [KNIFE-430](https://tickets.opscode.com/browse/KNIFE-430) server creation tunnelling should wait for a valid banner before continuing
107
+ * [KNIFE-381](https://tickets.opscode.com/browse/KNIFE-381) Gabriel Rosendorf Add ability to associate public ip with VPC
108
+ instance on creation
109
+
110
+ ## Releases prior to 0.8.0
111
+ Please see <https://github.com/chef/knife-ec2/commits> to view changes in
112
+ the form of commits to the source repository for releases before 0.8.0.
113
+
114
+
data/DOC_CHANGES.md CHANGED
@@ -1,17 +1,17 @@
1
- <!---
2
- This file is reset everytime when a new release is done. Contents of this file is for the currently unreleased version.
3
- -->
4
-
5
- # knife-ec2 doc changes
6
-
7
- Documentation changes are given below for **knife-ec2 version 0.14.0**.
8
-
9
- ## `knife ec2 server create` subcommand changes
10
-
11
- ### `--security-group-id` option
12
-
13
- The `--security-group-id` option allows the user to specify the security group id for server and is required when using VPC. Multiple security groups may be specified by using this option multiple times, e.g. `-g sg-e985168d -g sg-e7f06383 -g sg-ec1b7e88`.
14
-
15
- ### `--security-group-ids` option
16
-
17
- The previous option for specifying security groups, `--security-group-ids` (plural), is deprecated in favor of the `--security-group-id` option which mimics the more standard behavior for supplying multiple arguments across the ecosystem. This option will be removed in future release.
1
+ <!---
2
+ This file is reset everytime when a new release is done. Contents of this file is for the currently unreleased version.
3
+ -->
4
+
5
+ # knife-ec2 doc changes
6
+
7
+ Documentation changes are given below for **knife-ec2 version 0.17.0**.
8
+
9
+ ## `knife ec2 server create` subcommand changes
10
+
11
+ ### `--instance-initiated-shutdown-behavior` option
12
+
13
+ The `--instance-initiated-shutdown-behavior` option indicates whether an instance stops or terminates when you initiate shutdown from the instance. Possible values are 'stop' and 'terminate', default is 'stop'.
14
+
15
+ ### `--primary-eni` option
16
+
17
+ Specify a pre-existing ENI for primary interface when building the instance.
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
- source 'https://rubygems.org'
2
- gemspec
3
-
4
- gem 'rb-readline'
1
+ source 'https://rubygems.org'
2
+ gemspec
3
+
4
+ gem 'rb-readline', '>= 0.5.5'
data/RELEASE_NOTES.md CHANGED
@@ -1,36 +1,22 @@
1
- <!---
2
- This file is reset every time a new release is done. The contents of this file are for the currently unreleased version.
3
-
4
- Example Note:
5
-
6
- ## Example Heading
7
- Details about the thing that changed that needs to get included in the Release Notes in markdown.
8
- -->
9
-
10
- # knife-ec2 0.16.0 release notes:
11
- In this release we have added features for tagging EBS volumes and tagging node in Chef. There are a couple of bug fixes and enhancement as well.
12
-
13
- ## Features added in knife-ec2 0.16.0
14
-
15
- * Added support to tag node details to chef while node creation using `--tag-node-in-chef` PR: [492](https://github.com/chef/knife-ec2/pull/492).
16
-
17
- * Added support to tag EBS volumes while node creation using `--volume-tags Tag=Value[,Tag=Value...]` PR: [488](https://github.com/chef/knife-ec2/pull/488).
18
-
19
-
20
- ## Enhancement in knife-ec2 0.16.0
21
-
22
- * Update list of instance types that support ebs-encryption PR: [503](https://github.com/chef/knife-ec2/pull/503)
23
-
24
- * Enhanced Winrm cert to 10 year expiry PR: [496](https://github.com/chef/knife-ec2/pull/496).
25
-
26
- * Improper alignment of EC2 flavor list command `knife ec2 flavor list` PR: [490](https://github.com/chef/knife-ec2/pull/490)
27
-
28
- * Added new column description in EC2 AMIs list command `knife ec2 ami list` PR: [487](https://github.com/chef/knife-ec2/pull/487)
29
-
30
- ## Fixed issue in knife-ec2 0.16.0
31
-
32
- * Update bundler to resolve travis failure PR: [502](https://github.com/chef/knife-ec2/pull/502)
33
-
34
- * Fix issue Tag node in Chef PR: [492](https://github.com/chef/knife-ec2/pull/492) issue: [234](https://github.com/chef/knife-ec2/issues/234).
35
-
36
- * Added support to handle long passwords in windows PR: [489](https://github.com/chef/knife-ec2/pull/489) issue: [470](https://github.com/chef/knife-ec2/issues/470)
1
+ <!---
2
+ This file is reset every time a new release is done. The contents of this file are for the currently unreleased version.
3
+
4
+ Example Note:
5
+
6
+ ## Example Heading
7
+ Details about the thing that changed that needs to get included in the Release Notes in markdown.
8
+ -->
9
+
10
+ # knife-ec2 0.17.0 release notes:
11
+ In this release we have added features for adding exisiting ENI to server using `--primary-eni option`. Also added option `--instance-initiated-shutdown-behavior` to stop or terminate the instance on shutdown. There are a couple of bug fixes and enhancement as well.
12
+
13
+ ## Features added in knife-ec2 0.17.0
14
+
15
+ * Added support to add existing ENI to server while node creation using `--primary-eni` option. PR: [515](https://github.com/chef/knife-ec2/pull/515).
16
+
17
+ * Added support to set the `--instance-initiated-shutdown-behavior` with the option to set "stop" or "terminate" the instance on shutdown. The default is "stop". PR: [514](https://github.com/chef/knife-ec2/pull/514).
18
+
19
+
20
+ ## Enhancement in knife-ec2 0.17.0
21
+
22
+ * require `rb-readline` to avoid ruby 2.4 warnings about `Fixnum` PR: [513](https://github.com/chef/knife-ec2/pull/513)
@@ -1,1524 +1,1547 @@
1
- #
2
- # Author:: Adam Jacob (<adam@chef.io>)
3
- # Author:: Seth Chisamore (<schisamo@chef.io>)
4
- # Copyright:: Copyright (c) 2010-2015 Chef Software, Inc.
5
- # License:: Apache License, Version 2.0
6
- #
7
- # Licensed under the Apache License, Version 2.0 (the "License");
8
- # you may not use this file except in compliance with the License.
9
- # You may obtain a copy of the License at
10
- #
11
- # http://www.apache.org/licenses/LICENSE-2.0
12
- #
13
- # Unless required by applicable law or agreed to in writing, software
14
- # distributed under the License is distributed on an "AS IS" BASIS,
15
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
- # See the License for the specific language governing permissions and
17
- # limitations under the License.
18
- #
19
-
20
- require 'chef/knife/ec2_base'
21
- require 'chef/knife/s3_source'
22
- require 'chef/knife/winrm_base'
23
- require 'chef/knife/bootstrap_windows_base'
24
-
25
- class Chef
26
- class Knife
27
- class Ec2ServerCreate < Knife
28
-
29
- include Knife::Ec2Base
30
- include Knife::WinrmBase
31
- include Knife::BootstrapWindowsBase
32
- deps do
33
- require 'tempfile'
34
- require 'fog/aws'
35
- require 'uri'
36
- require 'readline'
37
- require 'chef/json_compat'
38
- require 'chef/knife/bootstrap'
39
- Chef::Knife::Bootstrap.load_deps
40
- end
41
-
42
- banner "knife ec2 server create (options)"
43
-
44
- attr_accessor :initial_sleep_delay
45
- attr_reader :server
46
-
47
- option :flavor,
48
- :short => "-f FLAVOR",
49
- :long => "--flavor FLAVOR",
50
- :description => "The flavor of server (m1.small, m1.medium, etc)",
51
- :proc => Proc.new { |f| Chef::Config[:knife][:flavor] = f }
52
-
53
- option :image,
54
- :short => "-I IMAGE",
55
- :long => "--image IMAGE",
56
- :description => "The AMI for the server",
57
- :proc => Proc.new { |i| Chef::Config[:knife][:image] = i }
58
-
59
- option :iam_instance_profile,
60
- :long => "--iam-profile NAME",
61
- :description => "The IAM instance profile to apply to this instance."
62
-
63
- option :security_groups,
64
- :short => "-G X,Y,Z",
65
- :long => "--groups X,Y,Z",
66
- :description => "The security groups for this server; not allowed when using VPC",
67
- :proc => Proc.new { |groups| groups.split(',') }
68
-
69
- option :security_group_ids,
70
- :long => "--security-group-ids 'X,Y,Z'",
71
- :description => "The security group ids for this server; required when using VPC. Provide values in format --security-group-ids 'X,Y,Z'. [DEPRECATED] This option will be removed in future release. Use the new --security-group-id option. ",
72
- :proc => Proc.new { |security_group_ids|
73
- ui.warn('[DEPRECATED] This option will be removed in future release. Use the new --security-group-id option multiple times when specifying multiple groups for e.g. -g sg-e985168d -g sg-e7f06383 -g sg-ec1b7e88.')
74
- if security_group_ids.gsub(' ', '').split(',').size > 1
75
- Chef::Config[:knife][:security_group_ids] = security_group_ids.gsub(' ', '').split(',')
76
- else
77
- Chef::Config[:knife][:security_group_ids] ||= []
78
- Chef::Config[:knife][:security_group_ids].push(security_group_ids)
79
- Chef::Config[:knife][:security_group_ids]
80
- end
81
- }
82
-
83
- option :security_group_id,
84
- :short => "-g SECURITY_GROUP_ID",
85
- :long => "--security-group-id ID",
86
- :description => "The security group id for this server; required when using VPC. Use the --security-group-id option multiple times when specifying multiple groups for e.g. -g sg-e985168d -g sg-e7f06383 -g sg-ec1b7e88.",
87
- :proc => Proc.new { |security_group_id|
88
- Chef::Config[:knife][:security_group_ids] ||= []
89
- Chef::Config[:knife][:security_group_ids].push(security_group_id)
90
- Chef::Config[:knife][:security_group_ids]
91
- }
92
-
93
- option :associate_eip,
94
- :long => "--associate-eip IP_ADDRESS",
95
- :description => "Associate existing elastic IP address with instance after launch"
96
-
97
- option :dedicated_instance,
98
- :long => "--dedicated_instance",
99
- :description => "Launch as a Dedicated instance (VPC ONLY)"
100
-
101
- option :placement_group,
102
- :long => "--placement-group PLACEMENT_GROUP",
103
- :description => "The placement group to place a cluster compute instance",
104
- :proc => Proc.new { |pg| Chef::Config[:knife][:placement_group] = pg }
105
-
106
- option :tags,
107
- :short => "-T T=V[,T=V,...]",
108
- :long => "--tags Tag=Value[,Tag=Value...]",
109
- :description => "The tags for this server",
110
- :proc => Proc.new { |tags| tags.split(',') }
111
-
112
- option :availability_zone,
113
- :short => "-Z ZONE",
114
- :long => "--availability-zone ZONE",
115
- :description => "The Availability Zone",
116
- :proc => Proc.new { |key| Chef::Config[:knife][:availability_zone] = key }
117
-
118
- option :chef_node_name,
119
- :short => "-N NAME",
120
- :long => "--node-name NAME",
121
- :description => "The Chef node name for your new node",
122
- :proc => Proc.new { |key| Chef::Config[:knife][:chef_node_name] = key }
123
-
124
- option :ssh_key_name,
125
- :short => "-S KEY",
126
- :long => "--ssh-key KEY",
127
- :description => "The AWS SSH key id",
128
- :proc => Proc.new { |key| Chef::Config[:knife][:ssh_key_name] = key }
129
-
130
- option :ssh_user,
131
- :short => "-x USERNAME",
132
- :long => "--ssh-user USERNAME",
133
- :description => "The ssh username",
134
- :default => "root"
135
-
136
- option :ssh_password,
137
- :short => "-P PASSWORD",
138
- :long => "--ssh-password PASSWORD",
139
- :description => "The ssh password"
140
-
141
- option :ssh_port,
142
- :short => "-p PORT",
143
- :long => "--ssh-port PORT",
144
- :description => "The ssh port",
145
- :default => "22",
146
- :proc => Proc.new { |key| Chef::Config[:knife][:ssh_port] = key }
147
-
148
- option :ssh_gateway,
149
- :short => "-w GATEWAY",
150
- :long => "--ssh-gateway GATEWAY",
151
- :description => "The ssh gateway server. Any proxies configured in your ssh config are automatically used by default.",
152
- :proc => Proc.new { |key| Chef::Config[:knife][:ssh_gateway] = key }
153
-
154
- option :ssh_gateway_identity,
155
- :long => "--ssh-gateway-identity IDENTITY_FILE",
156
- :description => "The private key for ssh gateway server",
157
- :proc => Proc.new { |key| Chef::Config[:knife][:ssh_gateway_identity] = key }
158
-
159
- option :identity_file,
160
- :short => "-i IDENTITY_FILE",
161
- :long => "--identity-file IDENTITY_FILE",
162
- :description => "The SSH identity file used for authentication"
163
-
164
- option :prerelease,
165
- :long => "--prerelease",
166
- :description => "Install the pre-release chef gems"
167
-
168
- option :bootstrap_version,
169
- :long => "--bootstrap-version VERSION",
170
- :description => "The version of Chef to install",
171
- :proc => Proc.new { |v| Chef::Config[:knife][:bootstrap_version] = v }
172
-
173
- option :bootstrap_proxy,
174
- :long => "--bootstrap-proxy PROXY_URL",
175
- :description => "The proxy server for the node being bootstrapped",
176
- :proc => Proc.new { |p| Chef::Config[:knife][:bootstrap_proxy] = p }
177
-
178
- option :distro,
179
- :short => "-d DISTRO",
180
- :long => "--distro DISTRO",
181
- :description => "Bootstrap a distro using a template. [DEPRECATED] Use --bootstrap-template option instead.",
182
- :proc => Proc.new { |v|
183
- Chef::Log.warn("[DEPRECATED] -d / --distro option is deprecated. Use --bootstrap-template option instead.")
184
- v
185
- }
186
-
187
- option :template_file,
188
- :long => "--template-file TEMPLATE",
189
- :description => "Full path to location of template to use. [DEPRECATED] Use -t / --bootstrap-template option instead.",
190
- :proc => Proc.new { |v|
191
- Chef::Log.warn("[DEPRECATED] --template-file option is deprecated. Use -t / --bootstrap-template option instead.")
192
- v
193
- }
194
-
195
- option :bootstrap_template,
196
- :long => "--bootstrap-template TEMPLATE",
197
- :description => "Bootstrap Chef using a built-in or custom template. Set to the full path of an erb template or use one of the built-in templates."
198
-
199
- option :ebs_size,
200
- :long => "--ebs-size SIZE",
201
- :description => "The size of the EBS volume in GB, for EBS-backed instances"
202
-
203
- option :ebs_optimized,
204
- :long => "--ebs-optimized",
205
- :description => "Enabled optimized EBS I/O"
206
-
207
- option :ebs_no_delete_on_term,
208
- :long => "--ebs-no-delete-on-term",
209
- :description => "Do not delete EBS volume on instance termination"
210
-
211
- option :run_list,
212
- :short => "-r RUN_LIST",
213
- :long => "--run-list RUN_LIST",
214
- :description => "Comma separated list of roles/recipes to apply",
215
- :proc => lambda { |o| o.split(/[\s,]+/) },
216
- :default => []
217
-
218
- option :secret,
219
- :long => "--secret ",
220
- :description => "The secret key to use to encrypt data bag item values",
221
- :proc => lambda { |s| Chef::Config[:knife][:secret] = s }
222
-
223
- option :secret_file,
224
- :long => "--secret-file SECRET_FILE",
225
- :description => "A file containing the secret key to use to encrypt data bag item values",
226
- :proc => lambda { |sf| Chef::Config[:knife][:secret_file] = sf }
227
-
228
- option :s3_secret,
229
- :long => '--s3-secret S3_SECRET_URL',
230
- :description => 'S3 URL (e.g. s3://bucket/file) for the encrypted_data_bag_secret_file',
231
- :proc => lambda { |url| Chef::Config[:knife][:s3_secret] = url }
232
-
233
- option :subnet_id,
234
- :long => "--subnet SUBNET-ID",
235
- :description => "create node in this Virtual Private Cloud Subnet ID (implies VPC mode)",
236
- :proc => Proc.new { |key| Chef::Config[:knife][:subnet_id] = key }
237
-
238
- option :private_ip_address,
239
- :long => "--private-ip-address IP-ADDRESS",
240
- :description => "allows to specify the private IP address of the instance in VPC mode",
241
- :proc => Proc.new { |ip| Chef::Config[:knife][:private_ip_address] = ip }
242
-
243
- option :host_key_verify,
244
- :long => "--[no-]host-key-verify",
245
- :description => "Verify host key, enabled by default.",
246
- :boolean => true,
247
- :default => true
248
-
249
- option :bootstrap_protocol,
250
- :long => "--bootstrap-protocol protocol",
251
- :description => "protocol to bootstrap windows servers. options: winrm/ssh",
252
- :proc => Proc.new { |key| Chef::Config[:knife][:bootstrap_protocol] = key },
253
- :default => nil
254
-
255
- option :fqdn,
256
- :long => "--fqdn FQDN",
257
- :description => "Pre-defined FQDN. This is used for Kerberos Authentication purpose only",
258
- :proc => Proc.new { |key| Chef::Config[:knife][:fqdn] = key },
259
- :default => nil
260
-
261
- option :aws_user_data,
262
- :long => "--user-data USER_DATA_FILE",
263
- :short => "-u USER_DATA_FILE",
264
- :description => "The EC2 User Data file to provision the instance with",
265
- :proc => Proc.new { |m| Chef::Config[:knife][:aws_user_data] = m },
266
- :default => nil
267
-
268
- option :hint,
269
- :long => "--hint HINT_NAME[=HINT_FILE]",
270
- :description => "Specify Ohai Hint to be set on the bootstrap target. Use multiple --hint options to specify multiple hints.",
271
- :proc => Proc.new { |h|
272
- Chef::Config[:knife][:hints] ||= {}
273
- name, path = h.split("=")
274
- Chef::Config[:knife][:hints][name] = path ? JSON.parse(::File.read(path)) : Hash.new
275
- }
276
-
277
- option :ephemeral,
278
- :long => "--ephemeral EPHEMERAL_DEVICES",
279
- :description => "Comma separated list of device locations (eg - /dev/sdb) to map ephemeral devices",
280
- :proc => lambda { |o| o.split(/[\s,]+/) },
281
- :default => []
282
-
283
- option :server_connect_attribute,
284
- :long => "--server-connect-attribute ATTRIBUTE",
285
- :short => "-a ATTRIBUTE",
286
- :description => "The EC2 server attribute to use for the SSH connection if necessary, e.g. public_ip_address or private_ip_address.",
287
- :default => nil
288
-
289
- option :associate_public_ip,
290
- :long => "--associate-public-ip",
291
- :description => "Associate public ip to VPC instance.",
292
- :boolean => true,
293
- :default => false
294
-
295
- option :ebs_volume_type,
296
- :long => "--ebs-volume-type TYPE",
297
- :description => "Possible values are standard (magnetic) | io1 | gp2 | sc1 | st1. Default is gp2",
298
- :proc => Proc.new { |key| Chef::Config[:knife][:ebs_volume_type] = key },
299
- :default => "gp2"
300
-
301
- option :ebs_provisioned_iops,
302
- :long => "--provisioned-iops IOPS",
303
- :description => "IOPS rate, only used when ebs volume type is 'io1'",
304
- :proc => Proc.new { |key| Chef::Config[:knife][:provisioned_iops] = key },
305
- :default => nil
306
-
307
- option :auth_timeout,
308
- :long => "--windows-auth-timeout MINUTES",
309
- :description => "The maximum time in minutes to wait to for authentication over the transport to the node to succeed. The default value is 25 minutes.",
310
- :default => 25
311
-
312
- option :validation_key_url,
313
- :long => "--validation-key-url URL",
314
- :description => "Path to the validation key",
315
- :proc => proc { |m| Chef::Config[:validation_key_url] = m }
316
-
317
- option :ebs_encrypted,
318
- :long => "--ebs-encrypted",
319
- :description => "Enables EBS volume encryption",
320
- :boolean => true,
321
- :default => false
322
-
323
- option :spot_price,
324
- :long => "--spot-price PRICE",
325
- :description => "The maximum hourly USD price for the instance",
326
- :default => nil
327
-
328
- option :spot_request_type,
329
- :long => "--spot-request-type TYPE",
330
- :description => "The Spot Instance request type. Possible values are 'one-time' and 'persistent', default value is 'one-time'",
331
- :default => "one-time"
332
-
333
- option :spot_wait_mode,
334
- :long => "--spot-wait-mode MODE",
335
- :description =>
336
- "Whether we should wait for spot request fulfillment. Could be 'wait', 'exit', or " \
337
- "'prompt' (default). For any of the above mentioned choices, ('wait') - if the " \
338
- "instance does not get allocated before the command itself times-out or ('exit') the " \
339
- "user needs to manually bootstrap the instance in the future after it gets allocated.",
340
- :default => "prompt"
341
-
342
- option :aws_connection_timeout,
343
- :long => "--aws-connection-timeout MINUTES",
344
- :description => "The maximum time in minutes to wait to for aws connection. Default is 10 min",
345
- :proc => proc {|t| t = t.to_i * 60; Chef::Config[:aws_connection_timeout] = t},
346
- :default => 600
347
-
348
- option :node_ssl_verify_mode,
349
- :long => "--node-ssl-verify-mode [peer|none]",
350
- :description => "Whether or not to verify the SSL cert for all HTTPS requests.",
351
- :proc => Proc.new { |v|
352
- valid_values = ["none", "peer"]
353
- unless valid_values.include?(v)
354
- raise "Invalid value '#{v}' for --node-ssl-verify-mode. Valid values are: #{valid_values.join(", ")}"
355
- end
356
- }
357
-
358
- option :node_verify_api_cert,
359
- :long => "--[no-]node-verify-api-cert",
360
- :description => "Verify the SSL cert for HTTPS requests to the Chef server API.",
361
- :boolean => true
362
-
363
- option :bootstrap_no_proxy,
364
- :long => "--bootstrap-no-proxy [NO_PROXY_URL|NO_PROXY_IP]",
365
- :description => "Do not proxy locations for the node being bootstrapped; this option is used internally by Opscode",
366
- :proc => Proc.new { |np| Chef::Config[:knife][:bootstrap_no_proxy] = np }
367
-
368
- option :bootstrap_url,
369
- :long => "--bootstrap-url URL",
370
- :description => "URL to a custom installation script",
371
- :proc => Proc.new { |u| Chef::Config[:knife][:bootstrap_url] = u }
372
-
373
- option :bootstrap_install_command,
374
- :long => "--bootstrap-install-command COMMANDS",
375
- :description => "Custom command to install chef-client",
376
- :proc => Proc.new { |ic| Chef::Config[:knife][:bootstrap_install_command] = ic }
377
-
378
- option :bootstrap_wget_options,
379
- :long => "--bootstrap-wget-options OPTIONS",
380
- :description => "Add options to wget when installing chef-client",
381
- :proc => Proc.new { |wo| Chef::Config[:knife][:bootstrap_wget_options] = wo }
382
-
383
- option :bootstrap_curl_options,
384
- :long => "--bootstrap-curl-options OPTIONS",
385
- :description => "Add options to curl when install chef-client",
386
- :proc => Proc.new { |co| Chef::Config[:knife][:bootstrap_curl_options] = co }
387
-
388
- option :bootstrap_vault_file,
389
- :long => '--bootstrap-vault-file VAULT_FILE',
390
- :description => 'A JSON file with a list of vault(s) and item(s) to be updated'
391
-
392
- option :bootstrap_vault_json,
393
- :long => '--bootstrap-vault-json VAULT_JSON',
394
- :description => 'A JSON string with the vault(s) and item(s) to be updated'
395
-
396
- option :bootstrap_vault_item,
397
- :long => '--bootstrap-vault-item VAULT_ITEM',
398
- :description => 'A single vault and item to update as "vault:item"',
399
- :proc => Proc.new { |i|
400
- (vault, item) = i.split(/:/)
401
- Chef::Config[:knife][:bootstrap_vault_item] ||= {}
402
- Chef::Config[:knife][:bootstrap_vault_item][vault] ||= []
403
- Chef::Config[:knife][:bootstrap_vault_item][vault].push(item)
404
- Chef::Config[:knife][:bootstrap_vault_item]
405
- }
406
-
407
- option :use_sudo_password,
408
- :long => "--use-sudo-password",
409
- :description => "Execute the bootstrap via sudo with password",
410
- :boolean => false
411
-
412
- option :forward_agent,
413
- :short => "-A",
414
- :long => "--forward-agent",
415
- :description => "Enable SSH agent forwarding",
416
- :boolean => true
417
-
418
- option :create_ssl_listener,
419
- :long => "--[no-]create-ssl-listener",
420
- :description => "Create ssl listener, enabled by default.",
421
- :boolean => true,
422
- :default => true
423
-
424
- option :network_interfaces,
425
- :short => '-n',
426
- :long => '--attach-network-interface ENI1,ENI2',
427
- :description => 'Attach additional network interfaces during bootstrap',
428
- :proc => proc { |nics| nics.split(',') }
429
-
430
- option :classic_link_vpc_id,
431
- :long => "--classic-link-vpc-id VPC_ID",
432
- :description => "Enable ClassicLink connection with a VPC"
433
-
434
- option :classic_link_vpc_security_group_ids,
435
- :long => "--classic-link-vpc-security-groups-ids X,Y,Z",
436
- :description => "Comma-separated list of security group ids for ClassicLink",
437
- :proc => Proc.new { |groups| groups.split(',') }
438
-
439
- option :disable_api_termination,
440
- :long => "--disable-api-termination",
441
- :description => "Disable termination of the instance using the Amazon EC2 console, CLI and API.",
442
- :boolean => true,
443
- :default => false
444
-
445
- option :volume_tags,
446
- :long => "--volume-tags Tag=Value[,Tag=Value...]",
447
- :description => "Tag the Root volume",
448
- :proc => Proc.new { |volume_tags| volume_tags.split(',') }
449
-
450
- option :tag_node_in_chef,
451
- :long => "--tag-node-in-chef",
452
- :description => "Flag for tagging node in ec2 and chef both",
453
- :boolean => true,
454
- :default => false
455
-
456
- def run
457
- $stdout.sync = true
458
- validate!
459
-
460
- requested_elastic_ip = config[:associate_eip] if config[:associate_eip]
461
-
462
- # For VPC EIP assignment we need the allocation ID so fetch full EIP details
463
- elastic_ip = connection.addresses.detect{|addr| addr if addr.public_ip == requested_elastic_ip}
464
-
465
- if locate_config_value(:spot_price)
466
- server_def = create_server_def
467
- server_def[:groups] = server_def[:security_group_ids] if vpc_mode?
468
- spot_request = connection.spot_requests.create(server_def)
469
- msg_pair("Spot Request ID", spot_request.id)
470
- msg_pair("Spot Request Type", spot_request.request_type)
471
- msg_pair("Spot Price", spot_request.price)
472
-
473
- case config[:spot_wait_mode]
474
- when 'prompt', '', nil
475
- wait_msg = "Do you want to wait for Spot Instance Request fulfillment? (Y/N) \n"
476
- wait_msg += "Y - Wait for Spot Instance request fulfillment\n"
477
- wait_msg += "N - Do not wait for Spot Instance request fulfillment. "
478
- wait_msg += ui.color("[WARN :: Request would be alive on AWS ec2 side but execution of Chef Bootstrap on the target instance will get skipped.]\n", :red, :bold)
479
- wait_msg += ui.color("\n[WARN :: For any of the above mentioned choices, (Y) - if the instance does not get allocated before the command itself times-out or (N) - user decides to exit, then in both cases user needs to manually bootstrap the instance in the future after it gets allocated.]\n\n", :cyan, :bold)
480
- confirm(wait_msg)
481
- when 'wait'
482
- # wait for the node and run Chef bootstrap
483
- when 'exit'
484
- ui.color("The 'exit' option was specified for --spot-wait-mode, exiting.", :cyan)
485
- exit
486
- else
487
- raise "Invalid value for --spot-wait-mode: '#{config[:spot_wait_mode]}', " \
488
- "valid values: wait, exit, prompt"
489
- end
490
-
491
- print ui.color("Waiting for Spot Request fulfillment: ", :cyan)
492
- spot_request.wait_for do
493
- @spinner ||= %w{| / - \\}
494
- print "\b" + @spinner.rotate!.first
495
- ready?
496
- end
497
- puts("\n")
498
- @server = connection.servers.get(spot_request.instance_id)
499
- else
500
- begin
501
- @server = connection.servers.create(create_server_def)
502
- rescue => error
503
- error.message.sub("download completed, but downloaded file not found", "Verify that you have public internet access.")
504
- ui.error error.message
505
- Chef::Log.debug("#{error.backtrace.join("\n")}")
506
- exit
507
- end
508
- end
509
-
510
- hashed_tags={}
511
- tags.map{ |t| key,val=t.split('='); hashed_tags[key]=val} unless tags.nil?
512
-
513
- # Always set the Name tag
514
- unless hashed_tags.keys.include? "Name"
515
- if locate_config_value(:chef_node_name)
516
- hashed_tags["Name"] = evaluate_node_name(locate_config_value(:chef_node_name))
517
- else
518
- hashed_tags["Name"] = server.id
519
- end
520
- end
521
-
522
- printed_tags = hashed_tags.map{ |tag, val| "#{tag}: #{val}" }.join(", ")
523
-
524
- hashed_volume_tags={}
525
- volume_tags = locate_config_value(:volume_tags)
526
- volume_tags.map{ |t| key,val=t.split('='); hashed_volume_tags[key]=val} unless volume_tags.nil?
527
- printed_volume_tags = hashed_volume_tags.map{ |tag, val| "#{tag}: #{val}" }.join(", ")
528
-
529
- msg_pair("Instance ID", @server.id)
530
- msg_pair("Flavor", @server.flavor_id)
531
- msg_pair("Image", @server.image_id)
532
- msg_pair("Region", connection.instance_variable_get(:@region))
533
- msg_pair("Availability Zone", @server.availability_zone)
534
-
535
- # If we don't specify a security group or security group id, Fog will
536
- # pick the appropriate default one. In case of a VPC we don't know the
537
- # default security group id at this point unless we look it up, hence
538
- # 'default' is printed if no id was specified.
539
- printed_security_groups = "default"
540
- printed_security_groups = @server.groups.join(", ") if @server.groups
541
- msg_pair("Security Groups", printed_security_groups) unless vpc_mode? or (@server.groups.nil? and @server.security_group_ids)
542
-
543
- printed_security_group_ids = "default"
544
- printed_security_group_ids = @server.security_group_ids.join(", ") if @server.security_group_ids
545
- msg_pair("Security Group Ids", printed_security_group_ids) if vpc_mode? or @server.security_group_ids
546
-
547
- msg_pair("IAM Profile", locate_config_value(:iam_instance_profile))
548
-
549
- msg_pair("Tags", printed_tags)
550
- msg_pair("Volume Tags", printed_volume_tags)
551
- msg_pair("SSH Key", @server.key_name)
552
-
553
- print "\n#{ui.color("Waiting for EC2 to create the instance", :magenta)}"
554
-
555
- # wait for instance to come up before acting against it
556
- @server.wait_for(locate_config_value(:aws_connection_timeout)) { print "."; ready? }
557
-
558
- puts("\n")
559
-
560
- # occasionally 'ready?' isn't, so retry a couple times if needed.
561
- tries = 6
562
- begin
563
- create_tags(hashed_tags) unless hashed_tags.empty?
564
- create_volume_tags(hashed_volume_tags) unless hashed_volume_tags.empty?
565
- associate_eip(elastic_ip) if config[:associate_eip]
566
- enable_classic_link(config[:classic_link_vpc_id], config[:classic_link_vpc_security_group_ids]) if config[:classic_link_vpc_id]
567
- rescue Fog::Compute::AWS::NotFound, Fog::Errors::Error
568
- raise if (tries -= 1) <= 0
569
- ui.warn("server not ready, retrying tag application (retries left: #{tries})")
570
- sleep 5
571
- retry
572
- end
573
-
574
- attach_nics if config[:network_interfaces]
575
-
576
- if vpc_mode?
577
- msg_pair("Subnet ID", @server.subnet_id)
578
- msg_pair("Tenancy", @server.tenancy)
579
- if config[:associate_public_ip]
580
- msg_pair("Public DNS Name", @server.dns_name)
581
- end
582
- if elastic_ip
583
- msg_pair("Public IP Address", @server.public_ip_address)
584
- end
585
- else
586
- msg_pair("Public DNS Name", @server.dns_name)
587
- msg_pair("Public IP Address", @server.public_ip_address)
588
- msg_pair("Private DNS Name", @server.private_dns_name)
589
- end
590
- msg_pair("Private IP Address", @server.private_ip_address)
591
-
592
- if Chef::Config[:knife][:validation_key_url]
593
- download_validation_key(validation_key_path)
594
- Chef::Config[:validation_key] = validation_key_path
595
- end
596
-
597
- #Check if Server is Windows or Linux
598
- if is_image_windows?
599
- protocol = locate_config_value(:bootstrap_protocol)
600
- protocol ||= 'winrm'
601
- if protocol == 'winrm'
602
- load_winrm_deps
603
- print "\n#{ui.color("Waiting for winrm access to become available", :magenta)}"
604
- print(".") until tcp_test_winrm(ssh_connect_host, locate_config_value(:winrm_port)) {
605
- sleep 10
606
- puts("done")
607
- }
608
- else
609
- print "\n#{ui.color("Waiting for sshd access to become available", :magenta)}"
610
- #If FreeSSHd, winsshd etc are available
611
- print(".") until tcp_test_ssh(ssh_connect_host, config[:ssh_port]) {
612
- sleep @initial_sleep_delay ||= (vpc_mode? ? 40 : 10)
613
- puts("done")
614
- }
615
- ssh_override_winrm
616
- end
617
- bootstrap_for_windows_node(@server, ssh_connect_host).run
618
- else
619
- print "\n#{ui.color("Waiting for sshd access to become available", :magenta)}"
620
- wait_for_sshd(ssh_connect_host)
621
- ssh_override_winrm
622
- bootstrap_for_linux_node(@server, ssh_connect_host).run
623
- end
624
-
625
- puts "\n"
626
- msg_pair("Instance ID", @server.id)
627
- msg_pair("Flavor", @server.flavor_id)
628
- msg_pair("Placement Group", @server.placement_group) unless @server.placement_group.nil?
629
- msg_pair("Image", @server.image_id)
630
- msg_pair("Region", connection.instance_variable_get(:@region))
631
- msg_pair("Availability Zone", @server.availability_zone)
632
- msg_pair("Security Groups", printed_security_groups) unless vpc_mode? or (@server.groups.nil? and @server.security_group_ids)
633
- msg_pair("Security Group Ids", printed_security_group_ids) if vpc_mode? or @server.security_group_ids
634
- msg_pair("IAM Profile", locate_config_value(:iam_instance_profile)) if locate_config_value(:iam_instance_profile)
635
- msg_pair("Tags", printed_tags)
636
- msg_pair("SSH Key", @server.key_name)
637
- msg_pair("Root Device Type", @server.root_device_type)
638
- msg_pair("Root Volume Tags", printed_volume_tags)
639
- if @server.root_device_type == "ebs"
640
- device_map = @server.block_device_mapping.first
641
- msg_pair("Root Volume ID", device_map['volumeId'])
642
- msg_pair("Root Device Name", device_map['deviceName'])
643
- msg_pair("Root Device Delete on Terminate", device_map['deleteOnTermination'])
644
- msg_pair("Standard or Provisioned IOPS", device_map['volumeType'])
645
- msg_pair("IOPS rate", device_map['iops'])
646
-
647
- print "\n#{ui.color("Block devices", :magenta)}\n"
648
- print "#{ui.color("===========================", :magenta)}\n"
649
- @server.block_device_mapping.each do |device_map|
650
- msg_pair("Device Name", device_map['deviceName'])
651
- msg_pair("Volume ID", device_map['volumeId'])
652
- msg_pair("Delete on Terminate", device_map['deleteOnTermination'].to_s)
653
- msg_pair("Standard or Provisioned IOPS", device_map['volumeType'])
654
- msg_pair("IOPS rate", device_map['iops'])
655
- print "\n"
656
- end
657
- print "#{ui.color("===========================", :magenta)}\n"
658
-
659
- if config[:ebs_size]
660
- if ami.block_device_mapping.first['volumeSize'].to_i < config[:ebs_size].to_i
661
- volume_too_large_warning = "#{config[:ebs_size]}GB " +
662
- "EBS volume size is larger than size set in AMI of " +
663
- "#{ami.block_device_mapping.first['volumeSize']}GB.\n" +
664
- "Use file system tools to make use of the increased volume size."
665
- msg_pair("Warning", volume_too_large_warning, :yellow)
666
- end
667
- end
668
- end
669
- if config[:ebs_optimized]
670
- msg_pair("EBS is Optimized", @server.ebs_optimized.to_s)
671
- end
672
- if vpc_mode?
673
- msg_pair("Subnet ID", @server.subnet_id)
674
- msg_pair("Tenancy", @server.tenancy)
675
- if config[:associate_public_ip]
676
- msg_pair("Public DNS Name", @server.dns_name)
677
- end
678
- else
679
- msg_pair("Public DNS Name", @server.dns_name)
680
- msg_pair("Public IP Address", @server.public_ip_address)
681
- msg_pair("Private DNS Name", @server.private_dns_name)
682
- end
683
- msg_pair("Private IP Address", @server.private_ip_address)
684
- msg_pair("Environment", config[:environment] || '_default')
685
- msg_pair("Run List", (config[:run_list] || []).join(', '))
686
- if config[:first_boot_attributes] || config[:first_boot_attributes_from_file]
687
- msg_pair("JSON Attributes",config[:first_boot_attributes] || config[:first_boot_attributes_from_file])
688
- end
689
- end
690
-
691
- def default_bootstrap_template
692
- is_image_windows? ? 'windows-chef-client-msi' : 'chef-full'
693
- end
694
-
695
- def validation_key_path
696
- @validation_key_path ||= begin
697
- if URI(Chef::Config[:knife][:validation_key_url]).scheme == 'file'
698
- URI(Chef::Config[:knife][:validation_key_url]).path
699
- else
700
- validation_key_tmpfile.path
701
- end
702
- end
703
- end
704
-
705
- def validation_key_tmpfile
706
- @validation_key_tmpfile ||= Tempfile.new('validation_key')
707
- end
708
-
709
- def download_validation_key(tempfile)
710
- Chef::Log.debug 'Downloading validation key ' \
711
- "<#{Chef::Config[:knife][:validation_key_url]}> to file " \
712
- "<#{tempfile}>"
713
-
714
- case URI(Chef::Config[:knife][:validation_key_url]).scheme
715
- when 's3'
716
- File.open(tempfile, 'w') { |f| f.write(s3_validation_key) }
717
- end
718
- end
719
-
720
- def s3_validation_key
721
- @s3_validation_key ||= begin
722
- Chef::Knife::S3Source.fetch(Chef::Config[:knife][:validation_key_url])
723
- end
724
- end
725
-
726
- def s3_secret
727
- @s3_secret ||= begin
728
- return false unless locate_config_value(:s3_secret)
729
- Chef::Knife::S3Source.fetch(locate_config_value(:s3_secret))
730
- end
731
- end
732
-
733
- def bootstrap_common_params(bootstrap)
734
- bootstrap.config[:run_list] = config[:run_list]
735
- bootstrap.config[:bootstrap_version] = locate_config_value(:bootstrap_version)
736
- bootstrap.config[:distro] = locate_config_value(:distro) || default_bootstrap_template
737
- # setting bootstrap_template value to template_file for backward compatibility
738
- bootstrap.config[:template_file] = locate_config_value(:template_file) || locate_config_value(:bootstrap_template)
739
- bootstrap.config[:environment] = locate_config_value(:environment)
740
- bootstrap.config[:prerelease] = config[:prerelease]
741
- bootstrap.config[:first_boot_attributes] = locate_config_value(:first_boot_attributes)
742
- bootstrap.config[:first_boot_attributes_from_file] = locate_config_value(:first_boot_attributes_from_file)
743
- bootstrap.config[:encrypted_data_bag_secret] = s3_secret || locate_config_value(:secret)
744
- bootstrap.config[:encrypted_data_bag_secret_file] = locate_config_value(:secret_file)
745
- # retrieving the secret from S3 is unique to knife-ec2, so we need to set "command line secret" to the value fetched from S3
746
- # When linux vm is spawned, the chef's secret option proc function sets the value "command line secret" and this value is used by
747
- # chef's code to check if secret option is passed through command line or not
748
- Chef::Knife::DataBagSecretOptions.set_cl_secret(s3_secret) if locate_config_value(:s3_secret)
749
- bootstrap.config[:secret] = s3_secret || locate_config_value(:secret)
750
- bootstrap.config[:secret_file] = locate_config_value(:secret_file)
751
- bootstrap.config[:node_ssl_verify_mode] = locate_config_value(:node_ssl_verify_mode)
752
- bootstrap.config[:node_verify_api_cert] = locate_config_value(:node_verify_api_cert)
753
- bootstrap.config[:bootstrap_no_proxy] = locate_config_value(:bootstrap_no_proxy)
754
- bootstrap.config[:bootstrap_url] = locate_config_value(:bootstrap_url)
755
- bootstrap.config[:bootstrap_install_command] = locate_config_value(:bootstrap_install_command)
756
- bootstrap.config[:bootstrap_wget_options] = locate_config_value(:bootstrap_wget_options)
757
- bootstrap.config[:bootstrap_curl_options] = locate_config_value(:bootstrap_curl_options)
758
- bootstrap.config[:bootstrap_vault_file] = locate_config_value(:bootstrap_vault_file)
759
- bootstrap.config[:bootstrap_vault_json] = locate_config_value(:bootstrap_vault_json)
760
- bootstrap.config[:bootstrap_vault_item] = locate_config_value(:bootstrap_vault_item)
761
- bootstrap.config[:use_sudo_password] = locate_config_value(:use_sudo_password)
762
- bootstrap.config[:yes] = locate_config_value(:yes)
763
- # Modify global configuration state to ensure hint gets set by
764
- # knife-bootstrap
765
- Chef::Config[:knife][:hints] ||= {}
766
- Chef::Config[:knife][:hints]["ec2"] ||= {}
767
- bootstrap
768
- end
769
-
770
- def fetch_server_fqdn(ip_addr)
771
- require 'resolv'
772
- Resolv.getname(ip_addr)
773
- end
774
-
775
- def bootstrap_for_windows_node(server, fqdn)
776
- if locate_config_value(:bootstrap_protocol) == 'winrm' || locate_config_value(:bootstrap_protocol) == nil
777
- if locate_config_value(:kerberos_realm)
778
- #Fetch AD/WINS based fqdn if any for Kerberos-based Auth
779
- fqdn = locate_config_value(:fqdn) || fetch_server_fqdn(server.private_ip_address)
780
- end
781
- bootstrap = Chef::Knife::BootstrapWindowsWinrm.new
782
- bootstrap.config[:winrm_user] = locate_config_value(:winrm_user)
783
- bootstrap.config[:winrm_password] = windows_password
784
- bootstrap.config[:winrm_transport] = locate_config_value(:winrm_transport)
785
- bootstrap.config[:kerberos_keytab_file] = locate_config_value(:kerberos_keytab_file)
786
- bootstrap.config[:kerberos_realm] = locate_config_value(:kerberos_realm)
787
- bootstrap.config[:kerberos_service] = locate_config_value(:kerberos_service)
788
- bootstrap.config[:ca_trust_file] = locate_config_value(:ca_trust_file)
789
- bootstrap.config[:winrm_port] = locate_config_value(:winrm_port)
790
- bootstrap.config[:auth_timeout] = locate_config_value(:auth_timeout)
791
- bootstrap.config[:winrm_ssl_verify_mode] = locate_config_value(:winrm_ssl_verify_mode)
792
- elsif locate_config_value(:bootstrap_protocol) == 'ssh'
793
- bootstrap = Chef::Knife::BootstrapWindowsSsh.new
794
- bootstrap.config[:ssh_user] = locate_config_value(:ssh_user)
795
- bootstrap.config[:ssh_password] = locate_config_value(:ssh_password)
796
- bootstrap.config[:ssh_port] = locate_config_value(:ssh_port)
797
- bootstrap.config[:identity_file] = locate_config_value(:identity_file)
798
- bootstrap.config[:no_host_key_verify] = locate_config_value(:no_host_key_verify)
799
- bootstrap.config[:forward_agent] = locate_config_value(:forward_agent)
800
- else
801
- ui.error("Unsupported Bootstrapping Protocol. Supported : winrm, ssh")
802
- exit 1
803
- end
804
- bootstrap.name_args = [fqdn]
805
- bootstrap.config[:msi_url] = locate_config_value(:msi_url)
806
- bootstrap.config[:install_as_service] = locate_config_value(:install_as_service)
807
- bootstrap.config[:session_timeout] = locate_config_value(:session_timeout)
808
- bootstrap.config[:tags] = config[:tags] if locate_config_value(:tag_node_in_chef)
809
-
810
- if locate_config_value(:chef_node_name)
811
- bootstrap.config[:chef_node_name] = evaluate_node_name(locate_config_value(:chef_node_name))
812
- else
813
- bootstrap.config[:chef_node_name] = server.id
814
- end
815
- bootstrap_common_params(bootstrap)
816
- end
817
-
818
- def bootstrap_for_linux_node(server,ssh_host)
819
- bootstrap = Chef::Knife::Bootstrap.new
820
- bootstrap.name_args = [ssh_host]
821
- bootstrap.config[:ssh_user] = config[:ssh_user]
822
- bootstrap.config[:ssh_password] = locate_config_value(:ssh_password)
823
- bootstrap.config[:ssh_port] = config[:ssh_port]
824
- bootstrap.config[:ssh_gateway] = config[:ssh_gateway]
825
- bootstrap.config[:identity_file] = config[:identity_file]
826
- bootstrap.config[:tags] = config[:tags] if locate_config_value(:tag_node_in_chef)
827
-
828
- if locate_config_value(:chef_node_name)
829
- bootstrap.config[:chef_node_name] = evaluate_node_name(locate_config_value(:chef_node_name))
830
- else
831
- bootstrap.config[:chef_node_name] = server.id
832
- end
833
- bootstrap.config[:use_sudo] = true unless config[:ssh_user] == 'root'
834
- # may be needed for vpc_mode
835
- bootstrap.config[:host_key_verify] = config[:host_key_verify]
836
- bootstrap_common_params(bootstrap)
837
- end
838
-
839
- def vpc_mode?
840
- # Amazon Virtual Private Cloud requires a subnet_id. If
841
- # present, do a few things differently
842
- !!locate_config_value(:subnet_id)
843
- end
844
-
845
- def ami
846
- @ami ||= connection.images.get(locate_config_value(:image))
847
- end
848
-
849
- def validate!
850
- if Chef::Config[:knife].keys.include? :aws_ssh_key_id
851
- Chef::Config[:knife][:ssh_key_name] = Chef::Config[:knife][:aws_ssh_key_id] if !Chef::Config[:knife][:ssh_key_name]
852
- Chef::Config[:knife].delete(:aws_ssh_key_id)
853
- ui.warn("Use of aws_ssh_key_id option in knife.rb config is deprecated, use ssh_key_name option instead.")
854
- end
855
-
856
- super([:image, :ssh_key_name, :aws_access_key_id, :aws_secret_access_key])
857
-
858
- validate_nics! if locate_config_value(:network_interfaces)
859
-
860
- if ami.nil?
861
- ui.error("You have not provided a valid image (AMI) value.")
862
- exit 1
863
- end
864
-
865
- if vpc_mode? and !!config[:security_groups]
866
- ui.error("You are using a VPC, security groups specified with '-G' are not allowed, specify one or more security group ids with '-g' instead.")
867
- exit 1
868
- end
869
-
870
- if !vpc_mode? and !!config[:private_ip_address]
871
- ui.error("You can only specify a private IP address if you are using VPC.")
872
- exit 1
873
- end
874
-
875
- if config[:dedicated_instance] and !vpc_mode?
876
- ui.error("You can only specify a Dedicated Instance if you are using VPC.")
877
- exit 1
878
- end
879
-
880
- if !vpc_mode? and config[:associate_public_ip]
881
- ui.error("--associate-public-ip option only applies to VPC instances, and you have not specified a subnet id.")
882
- exit 1
883
- end
884
-
885
- if config[:associate_eip]
886
- eips = connection.addresses.collect{|addr| addr if addr.domain == eip_scope}.compact
887
-
888
- unless eips.detect{|addr| addr.public_ip == config[:associate_eip] && addr.server_id == nil}
889
- ui.error("Elastic IP requested is not available.")
890
- exit 1
891
- end
892
- end
893
-
894
- if config[:ebs_provisioned_iops] and config[:ebs_volume_type] != 'io1'
895
- ui.error("--provisioned-iops option is only supported for volume type of 'io1'")
896
- exit 1
897
- end
898
-
899
- if config[:ebs_volume_type] == 'io1' and config[:ebs_provisioned_iops].nil?
900
- ui.error("--provisioned-iops option is required when using volume type of 'io1'")
901
- exit 1
902
- end
903
-
904
- if config[:ebs_volume_type] and ! %w(gp2 io1 standard).include?(config[:ebs_volume_type])
905
- ui.error("--ebs-volume-type must be 'standard' or 'io1' or 'gp2'")
906
- msg opt_parser
907
- exit 1
908
- end
909
-
910
- if config[:security_groups] && config[:security_groups].class == String
911
- ui.error("Invalid value type for knife[:security_groups] in knife configuration file (i.e knife.rb). Type should be array. e.g - knife[:security_groups] = ['sgroup1']")
912
- exit 1
913
- end
914
-
915
- # Validation for security_group_ids passed through knife.rb. It will raise error if values are not provided in Array.
916
- if locate_config_value(:security_group_ids) && locate_config_value(:security_group_ids).class == String
917
- ui.error("Invalid value type for knife[:security_group_ids] in knife configuration file (i.e knife.rb). Type should be array. e.g - knife[:security_group_ids] = ['sgroup1']")
918
- exit 1
919
- end
920
-
921
- if config[:classic_link_vpc_id].nil? ^ config[:classic_link_vpc_security_group_ids].nil?
922
- ui.error("--classic-link-vpc-id and --classic-link-vpc-security-group-ids must be used together")
923
- exit 1
924
- end
925
-
926
- if vpc_mode? and config[:classic_link_vpc_id]
927
- ui.error("You can only use ClassicLink if you are not using a VPC")
928
- exit 1
929
- end
930
-
931
- if locate_config_value(:ebs_encrypted)
932
- error_message = ""
933
- errors = []
934
- # validation for flavor and ebs_encrypted
935
- if !locate_config_value(:flavor)
936
- ui.error("--ebs-encrypted option requires valid flavor to be specified.")
937
- exit 1
938
- elsif (locate_config_value(:ebs_encrypted) and ! %w(m3.medium m3.large m3.xlarge m3.2xlarge m4.large m4.xlarge
939
- m4.2xlarge m4.4xlarge m4.10xlarge m4.16xlarge t2.nano t2.micro t2.small
940
- t2.medium t2.large t2.xlarge t2.2xlarge d2.xlarge d2.2xlarge d2.4xlarge
941
- d2.8xlarge c4.large c4.xlarge c4.2xlarge c4.4xlarge c4.8xlarge c3.large
942
- c3.xlarge c3.2xlarge c3.4xlarge c3.8xlarge cr1.8xlarge r3.large r3.xlarge
943
- r3.2xlarge r3.4xlarge r3.8xlarge r4.large r4.xlarge r4.2xlarge r4.4xlarge
944
- r4.8xlarge r4.16xlarge x1.16xlarge x1.32xlarge i2.xlarge i2.2xlarge i2.4xlarge
945
- i2.8xlarge i3.large i3.xlarge i3.2xlarge i3.4xlarge i3.8xlarge i3.16xlarge
946
- f1.2xlarge f1.16xlarge g2.2xlarge g2.8xlarge p2.xlarge p2.8xlarge p2.16xlarge).include?(locate_config_value(:flavor)))
947
- ui.error("--ebs-encrypted option is not supported for #{locate_config_value(:flavor)} flavor.")
948
- exit 1
949
- end
950
-
951
- # validation for ebs_size and ebs_volume_type and ebs_encrypted
952
- if !locate_config_value(:ebs_size)
953
- errors << "--ebs-encrypted option requires valid --ebs-size to be specified."
954
- elsif locate_config_value(:ebs_volume_type) == "gp2" and ! locate_config_value(:ebs_size).to_i.between?(1, 16384)
955
- errors << "--ebs-size should be in between 1-16384 for 'gp2' ebs volume type."
956
- elsif locate_config_value(:ebs_volume_type) == "io1" and ! locate_config_value(:ebs_size).to_i.between?(4, 16384)
957
- errors << "--ebs-size should be in between 4-16384 for 'io1' ebs volume type."
958
- elsif locate_config_value(:ebs_volume_type) == "standard" and ! locate_config_value(:ebs_size).to_i.between?(1, 1024)
959
- errors << "--ebs-size should be in between 1-1024 for 'standard' ebs volume type."
960
- end
961
-
962
- if errors.each{|e| error_message = "#{error_message} #{e}"}.any?
963
- ui.error(error_message)
964
- exit 1
965
- end
966
- end
967
-
968
- if locate_config_value(:spot_price) && locate_config_value(:disable_api_termination)
969
- ui.error("spot-price and disable-api-termination options cannot be passed together as 'Termination Protection' cannot be enabled for spot instances.")
970
- exit 1
971
- end
972
-
973
- if locate_config_value(:spot_price).nil? && locate_config_value(:spot_wait_mode).downcase != 'prompt'
974
- ui.error('spot-wait-mode option requires that a spot-price option is set.')
975
- exit 1
976
- end
977
-
978
- volume_tags = locate_config_value(:volume_tags)
979
- if !volume_tags.nil? and volume_tags.length != volume_tags.to_s.count('=')
980
- ui.error("Volume Tags should be entered in a key = value pair")
981
- exit 1
982
- end
983
-
984
- if (locate_config_value(:winrm_password).to_s.length > 14 )
985
- ui.warn("The password provided is longer than 14 characters. Computers with Windows prior to Windows 2000 will not be able to use this account. Do you want to continue this operation? (Y/N):")
986
- password_promt = STDIN.gets.chomp.upcase
987
- if (password_promt == "N")
988
- raise "Exiting as operation with password greater than 14 characters not accepted"
989
- elsif (password_promt == "Y")
990
- @allow_long_password = "/yes"
991
- else
992
- raise "The input provided is incorrect."
993
- end
994
- end
995
-
996
- end
997
-
998
- def tags
999
- tags = locate_config_value(:tags)
1000
- if !tags.nil? and tags.length != tags.to_s.count('=')
1001
- ui.error("Tags should be entered in a key = value pair")
1002
- exit 1
1003
- end
1004
- tags
1005
- end
1006
-
1007
- def eip_scope
1008
- if vpc_mode?
1009
- "vpc"
1010
- else
1011
- "standard"
1012
- end
1013
- end
1014
-
1015
- def ssl_config_user_data
1016
- user_related_commands = ""
1017
- winrm_user = locate_config_value(:winrm_user).split("\\")
1018
- if (winrm_user[0] == ".") || (winrm_user[0] == "") ||(winrm_user.length == 1)
1019
- user_related_commands = <<-EOH
1020
- net user /add #{locate_config_value(:winrm_user).delete('.\\')} #{windows_password} #{@allow_long_password};
1021
- net localgroup Administrators /add #{locate_config_value(:winrm_user).delete('.\\')};
1022
- EOH
1023
- end
1024
- <<-EOH
1025
- #{user_related_commands}
1026
- If (-Not (Get-Service WinRM | Where-Object {$_.status -eq "Running"})) {
1027
- winrm quickconfig -q
1028
- }
1029
- If (winrm e winrm/config/listener | Select-String -Pattern " Transport = HTTP\\b" -Quiet) {
1030
- winrm delete winrm/config/listener?Address=*+Transport=HTTP
1031
- }
1032
- $vm_name = invoke-restmethod -uri http://169.254.169.254/latest/meta-data/public-ipv4
1033
- If (-Not $vm_name) {
1034
- $vm_name = invoke-restmethod -uri http://169.254.169.254/latest/meta-data/local-ipv4
1035
- }
1036
-
1037
- $name = new-object -com "X509Enrollment.CX500DistinguishedName.1"
1038
- $name.Encode("CN=$vm_name", 0)
1039
- $key = new-object -com "X509Enrollment.CX509PrivateKey.1"
1040
- $key.ProviderName = "Microsoft RSA SChannel Cryptographic Provider"
1041
- $key.KeySpec = 1
1042
- $key.Length = 2048
1043
- $key.SecurityDescriptor = "D:PAI(A;;0xd01f01ff;;;SY)(A;;0xd01f01ff;;;BA)(A;;0x80120089;;;NS)"
1044
- $key.MachineContext = 1
1045
- $key.Create()
1046
- $serverauthoid = new-object -com "X509Enrollment.CObjectId.1"
1047
- $serverauthoid.InitializeFromValue("1.3.6.1.5.5.7.3.1")
1048
- $ekuoids = new-object -com "X509Enrollment.CObjectIds.1"
1049
- $ekuoids.add($serverauthoid)
1050
- $ekuext = new-object -com "X509Enrollment.CX509ExtensionEnhancedKeyUsage.1"
1051
- $ekuext.InitializeEncode($ekuoids)
1052
- $cert = new-object -com "X509Enrollment.CX509CertificateRequestCertificate.1"
1053
- $cert.InitializeFromPrivateKey(2, $key, "")
1054
- $cert.Subject = $name
1055
- $cert.Issuer = $cert.Subject
1056
- $cert.NotBefore = get-date
1057
- $cert.NotAfter = $cert.NotBefore.AddYears(10)
1058
- $cert.X509Extensions.Add($ekuext)
1059
- $cert.Encode()
1060
- $enrollment = new-object -com "X509Enrollment.CX509Enrollment.1"
1061
- $enrollment.InitializeFromRequest($cert)
1062
- $certdata = $enrollment.CreateRequest(0)
1063
- $enrollment.InstallResponse(2, $certdata, 0, "")
1064
-
1065
- $thumbprint = (Get-ChildItem -Path cert:\\localmachine\\my | Where-Object {$_.Subject -match "$vm_name"}).Thumbprint;
1066
- $create_listener_cmd = "winrm create winrm/config/Listener?Address=*+Transport=HTTPS '@{Hostname=`"$vm_name`";CertificateThumbprint=`"$thumbprint`"}'"
1067
- iex $create_listener_cmd
1068
- netsh advfirewall firewall add rule name="WinRM HTTPS" protocol=TCP dir=in Localport=5986 remoteport=any action=allow localip=any remoteip=any profile=any enable=yes
1069
- EOH
1070
- end
1071
-
1072
- def ssl_config_data_already_exist?
1073
- File.read(locate_config_value(:aws_user_data)).gsub(/\\\\/,"\\").include? ssl_config_user_data.strip
1074
- end
1075
-
1076
- def process_user_data(script_lines)
1077
- if !ssl_config_data_already_exist?
1078
- ps_start_tag = "<powershell>\n"
1079
- ps_end_tag = "</powershell>\n"
1080
- ps_start_tag_index = script_lines.index(ps_start_tag) || script_lines.index(ps_start_tag.strip)
1081
- ps_end_tag_index = script_lines.index(ps_end_tag) || script_lines.index(ps_end_tag.strip)
1082
- case
1083
- when ( ps_start_tag_index && !ps_end_tag_index ) || ( !ps_start_tag_index && ps_end_tag_index )
1084
- ui.error("Provided user_data file is invalid.")
1085
- exit 1
1086
- when ps_start_tag_index && ps_end_tag_index
1087
- script_lines[ps_end_tag_index] = ssl_config_user_data + ps_end_tag
1088
- when !ps_start_tag_index && !ps_end_tag_index
1089
- script_lines.insert(-1,"\n\n" + ps_start_tag + ssl_config_user_data + ps_end_tag)
1090
- end
1091
- end
1092
- script_lines
1093
- end
1094
-
1095
- def create_server_def
1096
- server_def = {
1097
- :image_id => locate_config_value(:image),
1098
- :groups => config[:security_groups],
1099
- :flavor_id => locate_config_value(:flavor),
1100
- :key_name => locate_config_value(:ssh_key_name),
1101
- :availability_zone => locate_config_value(:availability_zone),
1102
- :price => locate_config_value(:spot_price),
1103
- :request_type => locate_config_value(:spot_request_type)
1104
- }
1105
-
1106
- server_def[:security_group_ids] = locate_config_value(:security_group_ids)
1107
- server_def[:subnet_id] = locate_config_value(:subnet_id) if vpc_mode?
1108
- server_def[:private_ip_address] = locate_config_value(:private_ip_address) if vpc_mode?
1109
- server_def[:placement_group] = locate_config_value(:placement_group)
1110
- server_def[:iam_instance_profile_name] = locate_config_value(:iam_instance_profile)
1111
- server_def[:tenancy] = "dedicated" if vpc_mode? and locate_config_value(:dedicated_instance)
1112
- server_def[:associate_public_ip] = locate_config_value(:associate_public_ip) if vpc_mode? and config[:associate_public_ip]
1113
-
1114
- if locate_config_value(:winrm_transport) == 'ssl'
1115
- if locate_config_value(:aws_user_data)
1116
- begin
1117
- user_data = File.readlines(locate_config_value(:aws_user_data))
1118
- if config[:create_ssl_listener]
1119
- user_data = process_user_data(user_data)
1120
- end
1121
- user_data = user_data.join
1122
- server_def.merge!(:user_data => user_data)
1123
- rescue
1124
- ui.warn("Cannot read #{locate_config_value(:aws_user_data)}: #{$!.inspect}. Ignoring option.")
1125
- end
1126
- else
1127
- if config[:create_ssl_listener]
1128
- server_def.merge!(:user_data => "<powershell>\n" + ssl_config_user_data + "</powershell>\n")
1129
- end
1130
- end
1131
- else
1132
- if locate_config_value(:aws_user_data)
1133
- begin
1134
- server_def.merge!(:user_data => File.read(locate_config_value(:aws_user_data)))
1135
- rescue
1136
- ui.warn("Cannot read #{locate_config_value(:aws_user_data)}: #{$!.inspect}. Ignoring option.")
1137
- end
1138
- end
1139
- end
1140
-
1141
- if config[:ebs_optimized]
1142
- server_def[:ebs_optimized] = "true"
1143
- else
1144
- server_def[:ebs_optimized] = "false"
1145
- end
1146
-
1147
- if ami.root_device_type == "ebs"
1148
- if locate_config_value(:ebs_encrypted)
1149
- ami_map = ami.block_device_mapping[1]
1150
- else
1151
- ami_map = ami.block_device_mapping.first
1152
- end
1153
-
1154
- ebs_size = begin
1155
- if config[:ebs_size]
1156
- Integer(config[:ebs_size]).to_s
1157
- else
1158
- ami_map["volumeSize"].to_s
1159
- end
1160
- rescue ArgumentError
1161
- puts "--ebs-size must be an integer"
1162
- msg opt_parser
1163
- exit 1
1164
- end
1165
- delete_term = if config[:ebs_no_delete_on_term]
1166
- "false"
1167
- else
1168
- ami_map["deleteOnTermination"]
1169
- end
1170
- iops_rate = begin
1171
- if config[:ebs_provisioned_iops]
1172
- Integer(config[:ebs_provisioned_iops]).to_s
1173
- else
1174
- ami_map["iops"].to_s
1175
- end
1176
- rescue ArgumentError
1177
- puts "--provisioned-iops must be an integer"
1178
- msg opt_parser
1179
- exit 1
1180
- end
1181
-
1182
- server_def[:block_device_mapping] =
1183
- [{
1184
- 'DeviceName' => ami_map["deviceName"],
1185
- 'Ebs.VolumeSize' => ebs_size,
1186
- 'Ebs.DeleteOnTermination' => delete_term,
1187
- 'Ebs.VolumeType' => config[:ebs_volume_type],
1188
- }]
1189
- server_def[:block_device_mapping].first['Ebs.Iops'] = iops_rate unless iops_rate.empty?
1190
- server_def[:block_device_mapping].first['Ebs.Encrypted'] = true if locate_config_value(:ebs_encrypted)
1191
- end
1192
-
1193
- (config[:ephemeral] || []).each_with_index do |device_name, i|
1194
- server_def[:block_device_mapping] = (server_def[:block_device_mapping] || []) << {'VirtualName' => "ephemeral#{i}", 'DeviceName' => device_name}
1195
- end
1196
-
1197
- ## cannot pass disable_api_termination option to the API when using spot instances ##
1198
- server_def[:disable_api_termination] = locate_config_value(:disable_api_termination) if locate_config_value(:spot_price).nil?
1199
-
1200
- server_def
1201
- end
1202
-
1203
- def wait_for_sshd(hostname)
1204
- ssh_gateway = get_ssh_gateway_for(hostname)
1205
- ssh_gateway ? wait_for_tunnelled_sshd(ssh_gateway, hostname) : wait_for_direct_sshd(hostname, config[:ssh_port])
1206
- end
1207
-
1208
- def get_ssh_gateway_for(hostname)
1209
- if config[:ssh_gateway]
1210
- # The ssh_gateway specified in the knife config (if any) takes
1211
- # precedence over anything in the SSH configuration
1212
- Chef::Log.debug("Using ssh gateway #{config[:ssh_gateway]} from knife config")
1213
- config[:ssh_gateway]
1214
- else
1215
- # Next, check if the SSH configuration has a ProxyCommand
1216
- # directive for this host. If there is one, parse out the
1217
- # host from the proxy command
1218
- ssh_proxy = Net::SSH::Config.for(hostname)[:proxy]
1219
- if ssh_proxy.respond_to?(:command_line_template)
1220
- # ssh gateway_hostname nc %h %p
1221
- proxy_pattern = /ssh\s+(\S+)\s+nc/
1222
- matchdata = proxy_pattern.match(ssh_proxy.command_line_template)
1223
- if matchdata.nil?
1224
- Chef::Log.debug("Unable to determine ssh gateway for '#{hostname}' from ssh config template: #{ssh_proxy.command_line_template}")
1225
- nil
1226
- else
1227
- # Return hostname extracted from command line template
1228
- Chef::Log.debug("Using ssh gateway #{matchdata[1]} from ssh config")
1229
- matchdata[1]
1230
- end
1231
- else
1232
- # Return nil if we cannot find an ssh_gateway
1233
- Chef::Log.debug("No ssh gateway found, making a direct connection")
1234
- nil
1235
- end
1236
- end
1237
- end
1238
-
1239
- def wait_for_tunnelled_sshd(ssh_gateway, hostname)
1240
- initial = true
1241
- print(".") until tunnel_test_ssh(ssh_gateway, hostname) {
1242
- if initial
1243
- initial = false
1244
- sleep (vpc_mode? ? 40 : 10)
1245
- else
1246
- sleep 10
1247
- end
1248
- puts("done")
1249
- }
1250
- end
1251
-
1252
- def tunnel_test_ssh(ssh_gateway, hostname, &block)
1253
- status = false
1254
- gateway = configure_ssh_gateway(ssh_gateway)
1255
- gateway.open(hostname, config[:ssh_port]) do |local_tunnel_port|
1256
- status = tcp_test_ssh('localhost', local_tunnel_port, &block)
1257
- end
1258
- status
1259
- rescue SocketError, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ENETUNREACH, IOError
1260
- sleep 2
1261
- false
1262
- rescue Errno::EPERM, Errno::ETIMEDOUT
1263
- false
1264
- end
1265
-
1266
- def configure_ssh_gateway(ssh_gateway)
1267
- gw_host, gw_user = ssh_gateway.split('@').reverse
1268
- gw_host, gw_port = gw_host.split(':')
1269
- gateway_options = { :port => gw_port || 22 }
1270
-
1271
- # Load the SSH config for the SSH gateway host.
1272
- # Set the gateway user if it was not part of the
1273
- # SSH gateway string, and use any configured
1274
- # SSH keys.
1275
- ssh_gateway_config = Net::SSH::Config.for(gw_host)
1276
- gw_user ||= ssh_gateway_config[:user]
1277
-
1278
- # Always use the gateway keys from the SSH Config
1279
- gateway_keys = ssh_gateway_config[:keys]
1280
-
1281
- # Use the keys specificed on the command line if available (overrides SSH Config)
1282
- if config[:ssh_gateway_identity]
1283
- gateway_keys = Array(locate_config_value(:ssh_gateway_identity))
1284
- end
1285
-
1286
- unless gateway_keys.nil?
1287
- gateway_options[:keys] = gateway_keys
1288
- end
1289
-
1290
- Net::SSH::Gateway.new(gw_host, gw_user, gateway_options)
1291
- end
1292
-
1293
- def wait_for_direct_sshd(hostname, ssh_port)
1294
- initial = true
1295
- print(".") until tcp_test_ssh(hostname, ssh_port) {
1296
- if initial
1297
- initial = false
1298
- sleep (vpc_mode? ? 40 : 10)
1299
- else
1300
- sleep 10
1301
- end
1302
- puts("done")
1303
- }
1304
- end
1305
-
1306
- def subnet_public_ip_on_launch?
1307
- connection.subnets.get(server.subnet_id).map_public_ip_on_launch
1308
- end
1309
-
1310
- def ssh_connect_host
1311
- unless @ssh_connect_host
1312
- if config[:server_connect_attribute]
1313
- connect_attribute = config[:server_connect_attribute]
1314
- server.send(config[:server_connect_attribute])
1315
- elsif vpc_mode? && !(subnet_public_ip_on_launch? || config[:associate_public_ip] || config[:associate_eip])
1316
- connect_attribute = "private_ip_address"
1317
- server.private_ip_address
1318
- else
1319
- connect_attribute = server.dns_name ? "dns_name" : "public_ip_address"
1320
- server.send(connect_attribute)
1321
- end
1322
- @ssh_connect_host = server.send(connect_attribute)
1323
- end
1324
-
1325
- puts "\nSSH Target Address: #{@ssh_connect_host}(#{connect_attribute})"
1326
- @ssh_connect_host
1327
- end
1328
-
1329
- def create_tags(hashed_tags)
1330
- hashed_tags.each_pair do |key,val|
1331
- connection.tags.create :key => key, :value => val, :resource_id => @server.id
1332
- end
1333
- end
1334
-
1335
- def associate_eip(elastic_ip)
1336
- connection.associate_address(server.id, elastic_ip.public_ip, nil, elastic_ip.allocation_id)
1337
- @server.wait_for(locate_config_value(:aws_connection_timeout)) { public_ip_address == elastic_ip.public_ip }
1338
- end
1339
-
1340
- def validate_nics!
1341
- valid_nic_ids = connection.network_interfaces.all(
1342
- vpc_mode? ? { 'vpc-id' => vpc_id } : {}
1343
- ).map(&:network_interface_id)
1344
- invalid_nic_ids =
1345
- locate_config_value(:network_interfaces) - valid_nic_ids
1346
- return true if invalid_nic_ids.empty?
1347
- ui.error 'The following network interfaces are invalid: ' \
1348
- "#{invalid_nic_ids.join(', ')}"
1349
- exit 1
1350
- end
1351
-
1352
- def vpc_id
1353
- @vpc_id ||= begin
1354
- connection.subnets.get(locate_config_value(:subnet_id)).vpc_id
1355
- end
1356
- end
1357
-
1358
- def wait_for_nic_attachment
1359
- attached_nics_count = 0
1360
- until attached_nics_count ==
1361
- locate_config_value(:network_interfaces).count
1362
- attachment_nics =
1363
- locate_config_value(:network_interfaces).map do |nic_id|
1364
- connection.network_interfaces.get(nic_id).attachment['status']
1365
- end
1366
- attached_nics_count = attachment_nics.grep('attached').count
1367
- end
1368
- end
1369
-
1370
- def attach_nics
1371
- attachments = []
1372
- config[:network_interfaces].each_with_index do |nic_id, index|
1373
- attachments << connection.attach_network_interface(nic_id,
1374
- server.id,
1375
- index + 1).body
1376
- end
1377
- wait_for_nic_attachment
1378
- # rubocop:disable Style/RedundantReturn
1379
- return attachments
1380
- # rubocop:enable Style/RedundantReturn
1381
- end
1382
-
1383
- def enable_classic_link(vpc_id, security_group_ids)
1384
- connection.attach_classic_link_vpc(server.id, vpc_id, security_group_ids)
1385
- end
1386
-
1387
- def ssh_override_winrm
1388
- # unchanged ssh_user and changed winrm_user, override ssh_user
1389
- if locate_config_value(:ssh_user).eql?(options[:ssh_user][:default]) &&
1390
- !locate_config_value(:winrm_user).eql?(options[:winrm_user][:default])
1391
- config[:ssh_user] = locate_config_value(:winrm_user)
1392
- end
1393
- # unchanged ssh_port and changed winrm_port, override ssh_port
1394
- if locate_config_value(:ssh_port).eql?(options[:ssh_port][:default]) &&
1395
- !locate_config_value(:winrm_port).eql?(options[:winrm_port][:default])
1396
- config[:ssh_port] = locate_config_value(:winrm_port)
1397
- end
1398
- # unset ssh_password and set winrm_password, override ssh_password
1399
- if locate_config_value(:ssh_password).nil? &&
1400
- !locate_config_value(:winrm_password).nil?
1401
- config[:ssh_password] = locate_config_value(:winrm_password)
1402
- end
1403
- # unset identity_file and set kerberos_keytab_file, override identity_file
1404
- if locate_config_value(:identity_file).nil? &&
1405
- !locate_config_value(:kerberos_keytab_file).nil?
1406
- config[:identity_file] = locate_config_value(:kerberos_keytab_file)
1407
- end
1408
- end
1409
-
1410
- def tcp_test_winrm(ip_addr, port)
1411
- tcp_socket = TCPSocket.new(ip_addr, port)
1412
- yield
1413
- true
1414
- rescue SocketError
1415
- sleep 2
1416
- false
1417
- rescue Errno::ETIMEDOUT
1418
- false
1419
- rescue Errno::EPERM
1420
- false
1421
- rescue Errno::ECONNREFUSED
1422
- sleep 2
1423
- false
1424
- rescue Errno::EHOSTUNREACH
1425
- sleep 2
1426
- false
1427
- rescue Errno::ENETUNREACH
1428
- sleep 2
1429
- false
1430
- ensure
1431
- tcp_socket && tcp_socket.close
1432
- end
1433
-
1434
- def tcp_test_ssh(hostname, ssh_port)
1435
- tcp_socket = TCPSocket.new(hostname, ssh_port)
1436
- readable = IO.select([tcp_socket], nil, nil, 5)
1437
- if readable
1438
- ssh_banner = tcp_socket.gets
1439
- if ssh_banner.nil? or ssh_banner.empty?
1440
- false
1441
- else
1442
- Chef::Log.debug("sshd accepting connections on #{hostname}, banner is #{ssh_banner}")
1443
- yield
1444
- true
1445
- end
1446
- else
1447
- false
1448
- end
1449
- rescue SocketError, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ENETUNREACH, Errno::ENOTCONN, IOError
1450
- Chef::Log.debug("ssh failed to connect: #{hostname}")
1451
- sleep 2
1452
- false
1453
- rescue Errno::EPERM, Errno::ETIMEDOUT
1454
- Chef::Log.debug("ssh timed out: #{hostname}")
1455
- false
1456
- # This happens on some mobile phone networks
1457
- rescue Errno::ECONNRESET
1458
- Chef::Log.debug("ssh reset its connection: #{hostname}")
1459
- sleep 2
1460
- false
1461
- ensure
1462
- tcp_socket && tcp_socket.close
1463
- end
1464
-
1465
- def decrypt_admin_password(encoded_password, key)
1466
- require 'base64'
1467
- require 'openssl'
1468
- private_key = OpenSSL::PKey::RSA.new(key)
1469
- encrypted_password = Base64.decode64(encoded_password)
1470
- password = private_key.private_decrypt(encrypted_password)
1471
- password
1472
- end
1473
-
1474
- def check_windows_password_available(server_id)
1475
- sleep 10
1476
- response = connection.get_password_data(server_id)
1477
- if not response.body["passwordData"]
1478
- return false
1479
- end
1480
- response.body["passwordData"]
1481
- end
1482
-
1483
- def windows_password
1484
- if not locate_config_value(:winrm_password)
1485
- if locate_config_value(:identity_file)
1486
- print "\n#{ui.color("Waiting for Windows Admin password to be available", :magenta)}"
1487
- print(".") until check_windows_password_available(@server.id) {
1488
- puts("done")
1489
- }
1490
- response = connection.get_password_data(@server.id)
1491
- data = File.read(locate_config_value(:identity_file))
1492
- config[:winrm_password] = decrypt_admin_password(response.body["passwordData"], data)
1493
- else
1494
- ui.error("Cannot find SSH Identity file, required to fetch dynamically generated password")
1495
- exit 1
1496
- end
1497
- else
1498
- locate_config_value(:winrm_password)
1499
- end
1500
- end
1501
-
1502
- def load_winrm_deps
1503
- require 'winrm'
1504
- require 'chef/knife/winrm'
1505
- require 'chef/knife/bootstrap_windows_winrm'
1506
- require 'chef/knife/bootstrap_windows_ssh'
1507
- require 'chef/knife/core/windows_bootstrap_context'
1508
- end
1509
-
1510
- #Returns the name of node after evaluation of server id if %s is present.
1511
- #Eg: "Test-%s" will return "Test-i-12345" in case the instance id is i-12345
1512
- def evaluate_node_name(node_name)
1513
- return node_name%server.id
1514
- end
1515
-
1516
- def create_volume_tags(hashed_volume_tags)
1517
- hashed_volume_tags.each_pair do |key,val|
1518
- connection.tags.create :key => key, :value => val, :resource_id => @server.block_device_mapping.first['volumeId']
1519
- end
1520
- end
1521
-
1522
- end
1523
- end
1524
- end
1
+ #
2
+ # Author:: Adam Jacob (<adam@chef.io>)
3
+ # Author:: Seth Chisamore (<schisamo@chef.io>)
4
+ # Copyright:: Copyright (c) 2010-2015 Chef Software, Inc.
5
+ # License:: Apache License, Version 2.0
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+
20
+ require 'chef/knife/ec2_base'
21
+ require 'chef/knife/s3_source'
22
+ require 'chef/knife/winrm_base'
23
+ require 'chef/knife/bootstrap_windows_base'
24
+
25
+ class Chef
26
+ class Knife
27
+ class Ec2ServerCreate < Knife
28
+
29
+ include Knife::Ec2Base
30
+ include Knife::WinrmBase
31
+ include Knife::BootstrapWindowsBase
32
+ deps do
33
+ require 'tempfile'
34
+ require 'fog/aws'
35
+ require 'uri'
36
+ require 'readline'
37
+ require 'chef/json_compat'
38
+ require 'chef/knife/bootstrap'
39
+ Chef::Knife::Bootstrap.load_deps
40
+ end
41
+
42
+ banner "knife ec2 server create (options)"
43
+
44
+ attr_accessor :initial_sleep_delay
45
+ attr_reader :server
46
+
47
+ option :flavor,
48
+ :short => "-f FLAVOR",
49
+ :long => "--flavor FLAVOR",
50
+ :description => "The flavor of server (m1.small, m1.medium, etc)",
51
+ :proc => Proc.new { |f| Chef::Config[:knife][:flavor] = f }
52
+
53
+ option :image,
54
+ :short => "-I IMAGE",
55
+ :long => "--image IMAGE",
56
+ :description => "The AMI for the server",
57
+ :proc => Proc.new { |i| Chef::Config[:knife][:image] = i }
58
+
59
+ option :iam_instance_profile,
60
+ :long => "--iam-profile NAME",
61
+ :description => "The IAM instance profile to apply to this instance."
62
+
63
+ option :security_groups,
64
+ :short => "-G X,Y,Z",
65
+ :long => "--groups X,Y,Z",
66
+ :description => "The security groups for this server; not allowed when using VPC",
67
+ :proc => Proc.new { |groups| groups.split(',') }
68
+
69
+ option :security_group_ids,
70
+ :long => "--security-group-ids 'X,Y,Z'",
71
+ :description => "The security group ids for this server; required when using VPC. Provide values in format --security-group-ids 'X,Y,Z'. [DEPRECATED] This option will be removed in future release. Use the new --security-group-id option. ",
72
+ :proc => Proc.new { |security_group_ids|
73
+ ui.warn('[DEPRECATED] This option will be removed in future release. Use the new --security-group-id option multiple times when specifying multiple groups for e.g. -g sg-e985168d -g sg-e7f06383 -g sg-ec1b7e88.')
74
+ if security_group_ids.gsub(' ', '').split(',').size > 1
75
+ Chef::Config[:knife][:security_group_ids] = security_group_ids.gsub(' ', '').split(',')
76
+ else
77
+ Chef::Config[:knife][:security_group_ids] ||= []
78
+ Chef::Config[:knife][:security_group_ids].push(security_group_ids)
79
+ Chef::Config[:knife][:security_group_ids]
80
+ end
81
+ }
82
+
83
+ option :security_group_id,
84
+ :short => "-g SECURITY_GROUP_ID",
85
+ :long => "--security-group-id ID",
86
+ :description => "The security group id for this server; required when using VPC. Use the --security-group-id option multiple times when specifying multiple groups for e.g. -g sg-e985168d -g sg-e7f06383 -g sg-ec1b7e88.",
87
+ :proc => Proc.new { |security_group_id|
88
+ Chef::Config[:knife][:security_group_ids] ||= []
89
+ Chef::Config[:knife][:security_group_ids].push(security_group_id)
90
+ Chef::Config[:knife][:security_group_ids]
91
+ }
92
+
93
+ option :associate_eip,
94
+ :long => "--associate-eip IP_ADDRESS",
95
+ :description => "Associate existing elastic IP address with instance after launch"
96
+
97
+ option :dedicated_instance,
98
+ :long => "--dedicated_instance",
99
+ :description => "Launch as a Dedicated instance (VPC ONLY)"
100
+
101
+ option :placement_group,
102
+ :long => "--placement-group PLACEMENT_GROUP",
103
+ :description => "The placement group to place a cluster compute instance",
104
+ :proc => Proc.new { |pg| Chef::Config[:knife][:placement_group] = pg }
105
+
106
+ option :primary_eni,
107
+ :long => "--primary-eni ENI_ID",
108
+ :description => "Specify a pre-existing eni to use when building the instance."
109
+
110
+ option :tags,
111
+ :short => "-T T=V[,T=V,...]",
112
+ :long => "--tags Tag=Value[,Tag=Value...]",
113
+ :description => "The tags for this server",
114
+ :proc => Proc.new { |tags| tags.split(',') }
115
+
116
+ option :availability_zone,
117
+ :short => "-Z ZONE",
118
+ :long => "--availability-zone ZONE",
119
+ :description => "The Availability Zone",
120
+ :proc => Proc.new { |key| Chef::Config[:knife][:availability_zone] = key }
121
+
122
+ option :chef_node_name,
123
+ :short => "-N NAME",
124
+ :long => "--node-name NAME",
125
+ :description => "The Chef node name for your new node",
126
+ :proc => Proc.new { |key| Chef::Config[:knife][:chef_node_name] = key }
127
+
128
+ option :ssh_key_name,
129
+ :short => "-S KEY",
130
+ :long => "--ssh-key KEY",
131
+ :description => "The AWS SSH key id",
132
+ :proc => Proc.new { |key| Chef::Config[:knife][:ssh_key_name] = key }
133
+
134
+ option :ssh_user,
135
+ :short => "-x USERNAME",
136
+ :long => "--ssh-user USERNAME",
137
+ :description => "The ssh username",
138
+ :default => "root"
139
+
140
+ option :ssh_password,
141
+ :short => "-P PASSWORD",
142
+ :long => "--ssh-password PASSWORD",
143
+ :description => "The ssh password"
144
+
145
+ option :ssh_port,
146
+ :short => "-p PORT",
147
+ :long => "--ssh-port PORT",
148
+ :description => "The ssh port",
149
+ :default => "22",
150
+ :proc => Proc.new { |key| Chef::Config[:knife][:ssh_port] = key }
151
+
152
+ option :ssh_gateway,
153
+ :short => "-w GATEWAY",
154
+ :long => "--ssh-gateway GATEWAY",
155
+ :description => "The ssh gateway server. Any proxies configured in your ssh config are automatically used by default.",
156
+ :proc => Proc.new { |key| Chef::Config[:knife][:ssh_gateway] = key }
157
+
158
+ option :ssh_gateway_identity,
159
+ :long => "--ssh-gateway-identity IDENTITY_FILE",
160
+ :description => "The private key for ssh gateway server",
161
+ :proc => Proc.new { |key| Chef::Config[:knife][:ssh_gateway_identity] = key }
162
+
163
+ option :identity_file,
164
+ :short => "-i IDENTITY_FILE",
165
+ :long => "--identity-file IDENTITY_FILE",
166
+ :description => "The SSH identity file used for authentication"
167
+
168
+ option :prerelease,
169
+ :long => "--prerelease",
170
+ :description => "Install the pre-release chef gems"
171
+
172
+ option :bootstrap_version,
173
+ :long => "--bootstrap-version VERSION",
174
+ :description => "The version of Chef to install",
175
+ :proc => Proc.new { |v| Chef::Config[:knife][:bootstrap_version] = v }
176
+
177
+ option :bootstrap_proxy,
178
+ :long => "--bootstrap-proxy PROXY_URL",
179
+ :description => "The proxy server for the node being bootstrapped",
180
+ :proc => Proc.new { |p| Chef::Config[:knife][:bootstrap_proxy] = p }
181
+
182
+ option :distro,
183
+ :short => "-d DISTRO",
184
+ :long => "--distro DISTRO",
185
+ :description => "Bootstrap a distro using a template. [DEPRECATED] Use --bootstrap-template option instead.",
186
+ :proc => Proc.new { |v|
187
+ Chef::Log.warn("[DEPRECATED] -d / --distro option is deprecated. Use --bootstrap-template option instead.")
188
+ v
189
+ }
190
+
191
+ option :template_file,
192
+ :long => "--template-file TEMPLATE",
193
+ :description => "Full path to location of template to use. [DEPRECATED] Use -t / --bootstrap-template option instead.",
194
+ :proc => Proc.new { |v|
195
+ Chef::Log.warn("[DEPRECATED] --template-file option is deprecated. Use -t / --bootstrap-template option instead.")
196
+ v
197
+ }
198
+
199
+ option :bootstrap_template,
200
+ :long => "--bootstrap-template TEMPLATE",
201
+ :description => "Bootstrap Chef using a built-in or custom template. Set to the full path of an erb template or use one of the built-in templates."
202
+
203
+ option :ebs_size,
204
+ :long => "--ebs-size SIZE",
205
+ :description => "The size of the EBS volume in GB, for EBS-backed instances"
206
+
207
+ option :ebs_optimized,
208
+ :long => "--ebs-optimized",
209
+ :description => "Enabled optimized EBS I/O"
210
+
211
+ option :ebs_no_delete_on_term,
212
+ :long => "--ebs-no-delete-on-term",
213
+ :description => "Do not delete EBS volume on instance termination"
214
+
215
+ option :run_list,
216
+ :short => "-r RUN_LIST",
217
+ :long => "--run-list RUN_LIST",
218
+ :description => "Comma separated list of roles/recipes to apply",
219
+ :proc => lambda { |o| o.split(/[\s,]+/) },
220
+ :default => []
221
+
222
+ option :secret,
223
+ :long => "--secret ",
224
+ :description => "The secret key to use to encrypt data bag item values",
225
+ :proc => lambda { |s| Chef::Config[:knife][:secret] = s }
226
+
227
+ option :secret_file,
228
+ :long => "--secret-file SECRET_FILE",
229
+ :description => "A file containing the secret key to use to encrypt data bag item values",
230
+ :proc => lambda { |sf| Chef::Config[:knife][:secret_file] = sf }
231
+
232
+ option :s3_secret,
233
+ :long => '--s3-secret S3_SECRET_URL',
234
+ :description => 'S3 URL (e.g. s3://bucket/file) for the encrypted_data_bag_secret_file',
235
+ :proc => lambda { |url| Chef::Config[:knife][:s3_secret] = url }
236
+
237
+ option :subnet_id,
238
+ :long => "--subnet SUBNET-ID",
239
+ :description => "create node in this Virtual Private Cloud Subnet ID (implies VPC mode)",
240
+ :proc => Proc.new { |key| Chef::Config[:knife][:subnet_id] = key }
241
+
242
+ option :private_ip_address,
243
+ :long => "--private-ip-address IP-ADDRESS",
244
+ :description => "allows to specify the private IP address of the instance in VPC mode",
245
+ :proc => Proc.new { |ip| Chef::Config[:knife][:private_ip_address] = ip }
246
+
247
+ option :host_key_verify,
248
+ :long => "--[no-]host-key-verify",
249
+ :description => "Verify host key, enabled by default.",
250
+ :boolean => true,
251
+ :default => true
252
+
253
+ option :bootstrap_protocol,
254
+ :long => "--bootstrap-protocol protocol",
255
+ :description => "protocol to bootstrap windows servers. options: winrm/ssh",
256
+ :proc => Proc.new { |key| Chef::Config[:knife][:bootstrap_protocol] = key },
257
+ :default => nil
258
+
259
+ option :fqdn,
260
+ :long => "--fqdn FQDN",
261
+ :description => "Pre-defined FQDN. This is used for Kerberos Authentication purpose only",
262
+ :proc => Proc.new { |key| Chef::Config[:knife][:fqdn] = key },
263
+ :default => nil
264
+
265
+ option :aws_user_data,
266
+ :long => "--user-data USER_DATA_FILE",
267
+ :short => "-u USER_DATA_FILE",
268
+ :description => "The EC2 User Data file to provision the instance with",
269
+ :proc => Proc.new { |m| Chef::Config[:knife][:aws_user_data] = m },
270
+ :default => nil
271
+
272
+ option :hint,
273
+ :long => "--hint HINT_NAME[=HINT_FILE]",
274
+ :description => "Specify Ohai Hint to be set on the bootstrap target. Use multiple --hint options to specify multiple hints.",
275
+ :proc => Proc.new { |h|
276
+ Chef::Config[:knife][:hints] ||= {}
277
+ name, path = h.split("=")
278
+ Chef::Config[:knife][:hints][name] = path ? JSON.parse(::File.read(path)) : Hash.new
279
+ }
280
+
281
+ option :ephemeral,
282
+ :long => "--ephemeral EPHEMERAL_DEVICES",
283
+ :description => "Comma separated list of device locations (eg - /dev/sdb) to map ephemeral devices",
284
+ :proc => lambda { |o| o.split(/[\s,]+/) },
285
+ :default => []
286
+
287
+ option :server_connect_attribute,
288
+ :long => "--server-connect-attribute ATTRIBUTE",
289
+ :short => "-a ATTRIBUTE",
290
+ :description => "The EC2 server attribute to use for the SSH connection if necessary, e.g. public_ip_address or private_ip_address.",
291
+ :default => nil
292
+
293
+ option :associate_public_ip,
294
+ :long => "--associate-public-ip",
295
+ :description => "Associate public ip to VPC instance.",
296
+ :boolean => true,
297
+ :default => false
298
+
299
+ option :ebs_volume_type,
300
+ :long => "--ebs-volume-type TYPE",
301
+ :description => "Possible values are standard (magnetic) | io1 | gp2 | sc1 | st1. Default is gp2",
302
+ :proc => Proc.new { |key| Chef::Config[:knife][:ebs_volume_type] = key },
303
+ :default => "gp2"
304
+
305
+ option :ebs_provisioned_iops,
306
+ :long => "--provisioned-iops IOPS",
307
+ :description => "IOPS rate, only used when ebs volume type is 'io1'",
308
+ :proc => Proc.new { |key| Chef::Config[:knife][:provisioned_iops] = key },
309
+ :default => nil
310
+
311
+ option :auth_timeout,
312
+ :long => "--windows-auth-timeout MINUTES",
313
+ :description => "The maximum time in minutes to wait to for authentication over the transport to the node to succeed. The default value is 25 minutes.",
314
+ :default => 25
315
+
316
+ option :validation_key_url,
317
+ :long => "--validation-key-url URL",
318
+ :description => "Path to the validation key",
319
+ :proc => proc { |m| Chef::Config[:validation_key_url] = m }
320
+
321
+ option :ebs_encrypted,
322
+ :long => "--ebs-encrypted",
323
+ :description => "Enables EBS volume encryption",
324
+ :boolean => true,
325
+ :default => false
326
+
327
+ option :spot_price,
328
+ :long => "--spot-price PRICE",
329
+ :description => "The maximum hourly USD price for the instance",
330
+ :default => nil
331
+
332
+ option :spot_request_type,
333
+ :long => "--spot-request-type TYPE",
334
+ :description => "The Spot Instance request type. Possible values are 'one-time' and 'persistent', default value is 'one-time'",
335
+ :default => "one-time"
336
+
337
+ option :spot_wait_mode,
338
+ :long => "--spot-wait-mode MODE",
339
+ :description =>
340
+ "Whether we should wait for spot request fulfillment. Could be 'wait', 'exit', or " \
341
+ "'prompt' (default). For any of the above mentioned choices, ('wait') - if the " \
342
+ "instance does not get allocated before the command itself times-out or ('exit') the " \
343
+ "user needs to manually bootstrap the instance in the future after it gets allocated.",
344
+ :default => "prompt"
345
+
346
+ option :aws_connection_timeout,
347
+ :long => "--aws-connection-timeout MINUTES",
348
+ :description => "The maximum time in minutes to wait to for aws connection. Default is 10 min",
349
+ :proc => proc {|t| t = t.to_i * 60; Chef::Config[:aws_connection_timeout] = t},
350
+ :default => 600
351
+
352
+ option :node_ssl_verify_mode,
353
+ :long => "--node-ssl-verify-mode [peer|none]",
354
+ :description => "Whether or not to verify the SSL cert for all HTTPS requests.",
355
+ :proc => Proc.new { |v|
356
+ valid_values = ["none", "peer"]
357
+ unless valid_values.include?(v)
358
+ raise "Invalid value '#{v}' for --node-ssl-verify-mode. Valid values are: #{valid_values.join(", ")}"
359
+ end
360
+ }
361
+
362
+ option :node_verify_api_cert,
363
+ :long => "--[no-]node-verify-api-cert",
364
+ :description => "Verify the SSL cert for HTTPS requests to the Chef server API.",
365
+ :boolean => true
366
+
367
+ option :bootstrap_no_proxy,
368
+ :long => "--bootstrap-no-proxy [NO_PROXY_URL|NO_PROXY_IP]",
369
+ :description => "Do not proxy locations for the node being bootstrapped; this option is used internally by Opscode",
370
+ :proc => Proc.new { |np| Chef::Config[:knife][:bootstrap_no_proxy] = np }
371
+
372
+ option :bootstrap_url,
373
+ :long => "--bootstrap-url URL",
374
+ :description => "URL to a custom installation script",
375
+ :proc => Proc.new { |u| Chef::Config[:knife][:bootstrap_url] = u }
376
+
377
+ option :bootstrap_install_command,
378
+ :long => "--bootstrap-install-command COMMANDS",
379
+ :description => "Custom command to install chef-client",
380
+ :proc => Proc.new { |ic| Chef::Config[:knife][:bootstrap_install_command] = ic }
381
+
382
+ option :bootstrap_wget_options,
383
+ :long => "--bootstrap-wget-options OPTIONS",
384
+ :description => "Add options to wget when installing chef-client",
385
+ :proc => Proc.new { |wo| Chef::Config[:knife][:bootstrap_wget_options] = wo }
386
+
387
+ option :bootstrap_curl_options,
388
+ :long => "--bootstrap-curl-options OPTIONS",
389
+ :description => "Add options to curl when install chef-client",
390
+ :proc => Proc.new { |co| Chef::Config[:knife][:bootstrap_curl_options] = co }
391
+
392
+ option :bootstrap_vault_file,
393
+ :long => '--bootstrap-vault-file VAULT_FILE',
394
+ :description => 'A JSON file with a list of vault(s) and item(s) to be updated'
395
+
396
+ option :bootstrap_vault_json,
397
+ :long => '--bootstrap-vault-json VAULT_JSON',
398
+ :description => 'A JSON string with the vault(s) and item(s) to be updated'
399
+
400
+ option :bootstrap_vault_item,
401
+ :long => '--bootstrap-vault-item VAULT_ITEM',
402
+ :description => 'A single vault and item to update as "vault:item"',
403
+ :proc => Proc.new { |i|
404
+ (vault, item) = i.split(/:/)
405
+ Chef::Config[:knife][:bootstrap_vault_item] ||= {}
406
+ Chef::Config[:knife][:bootstrap_vault_item][vault] ||= []
407
+ Chef::Config[:knife][:bootstrap_vault_item][vault].push(item)
408
+ Chef::Config[:knife][:bootstrap_vault_item]
409
+ }
410
+
411
+ option :use_sudo_password,
412
+ :long => "--use-sudo-password",
413
+ :description => "Execute the bootstrap via sudo with password",
414
+ :boolean => false
415
+
416
+ option :forward_agent,
417
+ :short => "-A",
418
+ :long => "--forward-agent",
419
+ :description => "Enable SSH agent forwarding",
420
+ :boolean => true
421
+
422
+ option :create_ssl_listener,
423
+ :long => "--[no-]create-ssl-listener",
424
+ :description => "Create ssl listener, enabled by default.",
425
+ :boolean => true,
426
+ :default => true
427
+
428
+ option :network_interfaces,
429
+ :short => '-n',
430
+ :long => '--attach-network-interface ENI1,ENI2',
431
+ :description => 'Attach additional network interfaces during bootstrap',
432
+ :proc => proc { |nics| nics.split(',') }
433
+
434
+ option :classic_link_vpc_id,
435
+ :long => "--classic-link-vpc-id VPC_ID",
436
+ :description => "Enable ClassicLink connection with a VPC"
437
+
438
+ option :classic_link_vpc_security_group_ids,
439
+ :long => "--classic-link-vpc-security-groups-ids X,Y,Z",
440
+ :description => "Comma-separated list of security group ids for ClassicLink",
441
+ :proc => Proc.new { |groups| groups.split(',') }
442
+
443
+ option :disable_api_termination,
444
+ :long => "--disable-api-termination",
445
+ :description => "Disable termination of the instance using the Amazon EC2 console, CLI and API.",
446
+ :boolean => true,
447
+ :default => false
448
+
449
+ option :volume_tags,
450
+ :long => "--volume-tags Tag=Value[,Tag=Value...]",
451
+ :description => "Tag the Root volume",
452
+ :proc => Proc.new { |volume_tags| volume_tags.split(',') }
453
+
454
+ option :tag_node_in_chef,
455
+ :long => "--tag-node-in-chef",
456
+ :description => "Flag for tagging node in ec2 and chef both",
457
+ :boolean => true,
458
+ :default => false
459
+
460
+ option :instance_initiated_shutdown_behavior,
461
+ :long => "--instance-initiated-shutdown-behavior SHUTDOWN_BEHAVIOR",
462
+ :description => "Indicates whether an instance stops or terminates when you initiate shutdown from the instance. Possible values are 'stop' and 'terminate', default is 'stop'."
463
+
464
+ def run
465
+ $stdout.sync = true
466
+ validate!
467
+
468
+ requested_elastic_ip = config[:associate_eip] if config[:associate_eip]
469
+
470
+ # For VPC EIP assignment we need the allocation ID so fetch full EIP details
471
+ elastic_ip = connection.addresses.detect{|addr| addr if addr.public_ip == requested_elastic_ip}
472
+
473
+ if locate_config_value(:spot_price)
474
+ server_def = create_server_def
475
+ server_def[:groups] = server_def[:security_group_ids] if vpc_mode?
476
+ spot_request = connection.spot_requests.create(server_def)
477
+ msg_pair("Spot Request ID", spot_request.id)
478
+ msg_pair("Spot Request Type", spot_request.request_type)
479
+ msg_pair("Spot Price", spot_request.price)
480
+
481
+ case config[:spot_wait_mode]
482
+ when 'prompt', '', nil
483
+ wait_msg = "Do you want to wait for Spot Instance Request fulfillment? (Y/N) \n"
484
+ wait_msg += "Y - Wait for Spot Instance request fulfillment\n"
485
+ wait_msg += "N - Do not wait for Spot Instance request fulfillment. "
486
+ wait_msg += ui.color("[WARN :: Request would be alive on AWS ec2 side but execution of Chef Bootstrap on the target instance will get skipped.]\n", :red, :bold)
487
+ wait_msg += ui.color("\n[WARN :: For any of the above mentioned choices, (Y) - if the instance does not get allocated before the command itself times-out or (N) - user decides to exit, then in both cases user needs to manually bootstrap the instance in the future after it gets allocated.]\n\n", :cyan, :bold)
488
+ confirm(wait_msg)
489
+ when 'wait'
490
+ # wait for the node and run Chef bootstrap
491
+ when 'exit'
492
+ ui.color("The 'exit' option was specified for --spot-wait-mode, exiting.", :cyan)
493
+ exit
494
+ else
495
+ raise "Invalid value for --spot-wait-mode: '#{config[:spot_wait_mode]}', " \
496
+ "valid values: wait, exit, prompt"
497
+ end
498
+
499
+ print ui.color("Waiting for Spot Request fulfillment: ", :cyan)
500
+ spot_request.wait_for do
501
+ @spinner ||= %w{| / - \\}
502
+ print "\b" + @spinner.rotate!.first
503
+ ready?
504
+ end
505
+ puts("\n")
506
+ @server = connection.servers.get(spot_request.instance_id)
507
+ else
508
+ begin
509
+ @server = connection.servers.create(create_server_def)
510
+ rescue => error
511
+ error.message.sub("download completed, but downloaded file not found", "Verify that you have public internet access.")
512
+ ui.error error.message
513
+ Chef::Log.debug("#{error.backtrace.join("\n")}")
514
+ exit
515
+ end
516
+ end
517
+
518
+ hashed_tags={}
519
+ tags.map{ |t| key,val=t.split('='); hashed_tags[key]=val} unless tags.nil?
520
+
521
+ # Always set the Name tag
522
+ unless hashed_tags.keys.include? "Name"
523
+ if locate_config_value(:chef_node_name)
524
+ hashed_tags["Name"] = evaluate_node_name(locate_config_value(:chef_node_name))
525
+ else
526
+ hashed_tags["Name"] = server.id
527
+ end
528
+ end
529
+
530
+ printed_tags = hashed_tags.map{ |tag, val| "#{tag}: #{val}" }.join(", ")
531
+
532
+ hashed_volume_tags={}
533
+ volume_tags = locate_config_value(:volume_tags)
534
+ volume_tags.map{ |t| key,val=t.split('='); hashed_volume_tags[key]=val} unless volume_tags.nil?
535
+ printed_volume_tags = hashed_volume_tags.map{ |tag, val| "#{tag}: #{val}" }.join(", ")
536
+
537
+ msg_pair("Instance ID", @server.id)
538
+ msg_pair("Flavor", @server.flavor_id)
539
+ msg_pair("Image", @server.image_id)
540
+ msg_pair("Region", connection.instance_variable_get(:@region))
541
+ msg_pair("Availability Zone", @server.availability_zone)
542
+
543
+ # If we don't specify a security group or security group id, Fog will
544
+ # pick the appropriate default one. In case of a VPC we don't know the
545
+ # default security group id at this point unless we look it up, hence
546
+ # 'default' is printed if no id was specified.
547
+ printed_security_groups = "default"
548
+ printed_security_groups = @server.groups.join(", ") if @server.groups
549
+ msg_pair("Security Groups", printed_security_groups) unless vpc_mode? or (@server.groups.nil? and @server.security_group_ids)
550
+
551
+ printed_security_group_ids = "default"
552
+ printed_security_group_ids = @server.security_group_ids.join(", ") if @server.security_group_ids
553
+ msg_pair("Security Group Ids", printed_security_group_ids) if vpc_mode? or @server.security_group_ids
554
+
555
+ msg_pair("IAM Profile", locate_config_value(:iam_instance_profile))
556
+
557
+ msg_pair("Tags", printed_tags)
558
+ msg_pair("Volume Tags", printed_volume_tags)
559
+ msg_pair("SSH Key", @server.key_name)
560
+
561
+ print "\n#{ui.color("Waiting for EC2 to create the instance", :magenta)}"
562
+
563
+ # wait for instance to come up before acting against it
564
+ @server.wait_for(locate_config_value(:aws_connection_timeout)) { print "."; ready? }
565
+
566
+ puts("\n")
567
+
568
+ # occasionally 'ready?' isn't, so retry a couple times if needed.
569
+ tries = 6
570
+ begin
571
+ create_tags(hashed_tags) unless hashed_tags.empty?
572
+ create_volume_tags(hashed_volume_tags) unless hashed_volume_tags.empty?
573
+ associate_eip(elastic_ip) if config[:associate_eip]
574
+ enable_classic_link(config[:classic_link_vpc_id], config[:classic_link_vpc_security_group_ids]) if config[:classic_link_vpc_id]
575
+ rescue Fog::Compute::AWS::NotFound, Fog::Errors::Error
576
+ raise if (tries -= 1) <= 0
577
+ ui.warn("server not ready, retrying tag application (retries left: #{tries})")
578
+ sleep 5
579
+ retry
580
+ end
581
+
582
+ attach_nics if config[:network_interfaces]
583
+
584
+ if vpc_mode?
585
+ msg_pair("Subnet ID", @server.subnet_id)
586
+ msg_pair("Tenancy", @server.tenancy)
587
+ if config[:associate_public_ip]
588
+ msg_pair("Public DNS Name", @server.dns_name)
589
+ end
590
+ if elastic_ip
591
+ msg_pair("Public IP Address", @server.public_ip_address)
592
+ end
593
+ else
594
+ msg_pair("Public DNS Name", @server.dns_name)
595
+ msg_pair("Public IP Address", @server.public_ip_address)
596
+ msg_pair("Private DNS Name", @server.private_dns_name)
597
+ end
598
+ msg_pair("Private IP Address", @server.private_ip_address)
599
+
600
+ if Chef::Config[:knife][:validation_key_url]
601
+ download_validation_key(validation_key_path)
602
+ Chef::Config[:validation_key] = validation_key_path
603
+ end
604
+
605
+ #Check if Server is Windows or Linux
606
+ if is_image_windows?
607
+ protocol = locate_config_value(:bootstrap_protocol)
608
+ protocol ||= 'winrm'
609
+ if protocol == 'winrm'
610
+ load_winrm_deps
611
+ print "\n#{ui.color("Waiting for winrm access to become available", :magenta)}"
612
+ print(".") until tcp_test_winrm(ssh_connect_host, locate_config_value(:winrm_port)) {
613
+ sleep 10
614
+ puts("done")
615
+ }
616
+ else
617
+ print "\n#{ui.color("Waiting for sshd access to become available", :magenta)}"
618
+ #If FreeSSHd, winsshd etc are available
619
+ print(".") until tcp_test_ssh(ssh_connect_host, config[:ssh_port]) {
620
+ sleep @initial_sleep_delay ||= (vpc_mode? ? 40 : 10)
621
+ puts("done")
622
+ }
623
+ ssh_override_winrm
624
+ end
625
+ bootstrap_for_windows_node(@server, ssh_connect_host).run
626
+ else
627
+ print "\n#{ui.color("Waiting for sshd access to become available", :magenta)}"
628
+ wait_for_sshd(ssh_connect_host)
629
+ ssh_override_winrm
630
+ bootstrap_for_linux_node(@server, ssh_connect_host).run
631
+ end
632
+
633
+ puts "\n"
634
+ msg_pair("Instance ID", @server.id)
635
+ msg_pair("Flavor", @server.flavor_id)
636
+ msg_pair("Placement Group", @server.placement_group) unless @server.placement_group.nil?
637
+ msg_pair("Image", @server.image_id)
638
+ msg_pair("Region", connection.instance_variable_get(:@region))
639
+ msg_pair("Availability Zone", @server.availability_zone)
640
+ msg_pair("Security Groups", printed_security_groups) unless vpc_mode? or (@server.groups.nil? and @server.security_group_ids)
641
+ msg_pair("Security Group Ids", printed_security_group_ids) if vpc_mode? or @server.security_group_ids
642
+ msg_pair("IAM Profile", locate_config_value(:iam_instance_profile)) if locate_config_value(:iam_instance_profile)
643
+ msg_pair("Primary ENI", locate_config_value(:primary_eni)) if locate_config_value(:primary_eni)
644
+ msg_pair("Tags", printed_tags)
645
+ msg_pair("SSH Key", @server.key_name)
646
+ msg_pair("Root Device Type", @server.root_device_type)
647
+ msg_pair("Root Volume Tags", printed_volume_tags)
648
+ if @server.root_device_type == "ebs"
649
+ device_map = @server.block_device_mapping.first
650
+ msg_pair("Root Volume ID", device_map['volumeId'])
651
+ msg_pair("Root Device Name", device_map['deviceName'])
652
+ msg_pair("Root Device Delete on Terminate", device_map['deleteOnTermination'])
653
+ msg_pair("Standard or Provisioned IOPS", device_map['volumeType'])
654
+ msg_pair("IOPS rate", device_map['iops'])
655
+
656
+ print "\n#{ui.color("Block devices", :magenta)}\n"
657
+ print "#{ui.color("===========================", :magenta)}\n"
658
+ @server.block_device_mapping.each do |device_map|
659
+ msg_pair("Device Name", device_map['deviceName'])
660
+ msg_pair("Volume ID", device_map['volumeId'])
661
+ msg_pair("Delete on Terminate", device_map['deleteOnTermination'].to_s)
662
+ msg_pair("Standard or Provisioned IOPS", device_map['volumeType'])
663
+ msg_pair("IOPS rate", device_map['iops'])
664
+ print "\n"
665
+ end
666
+ print "#{ui.color("===========================", :magenta)}\n"
667
+
668
+ if config[:ebs_size]
669
+ if ami.block_device_mapping.first['volumeSize'].to_i < config[:ebs_size].to_i
670
+ volume_too_large_warning = "#{config[:ebs_size]}GB " +
671
+ "EBS volume size is larger than size set in AMI of " +
672
+ "#{ami.block_device_mapping.first['volumeSize']}GB.\n" +
673
+ "Use file system tools to make use of the increased volume size."
674
+ msg_pair("Warning", volume_too_large_warning, :yellow)
675
+ end
676
+ end
677
+ end
678
+ if config[:ebs_optimized]
679
+ msg_pair("EBS is Optimized", @server.ebs_optimized.to_s)
680
+ end
681
+ if vpc_mode?
682
+ msg_pair("Subnet ID", @server.subnet_id)
683
+ msg_pair("Tenancy", @server.tenancy)
684
+ if config[:associate_public_ip]
685
+ msg_pair("Public DNS Name", @server.dns_name)
686
+ end
687
+ else
688
+ msg_pair("Public DNS Name", @server.dns_name)
689
+ msg_pair("Public IP Address", @server.public_ip_address)
690
+ msg_pair("Private DNS Name", @server.private_dns_name)
691
+ end
692
+ msg_pair("Private IP Address", @server.private_ip_address)
693
+ msg_pair("Environment", config[:environment] || '_default')
694
+ msg_pair("Run List", (config[:run_list] || []).join(', '))
695
+ if config[:first_boot_attributes] || config[:first_boot_attributes_from_file]
696
+ msg_pair("JSON Attributes",config[:first_boot_attributes] || config[:first_boot_attributes_from_file])
697
+ end
698
+ end
699
+
700
+ def default_bootstrap_template
701
+ is_image_windows? ? 'windows-chef-client-msi' : 'chef-full'
702
+ end
703
+
704
+ def validation_key_path
705
+ @validation_key_path ||= begin
706
+ if URI(Chef::Config[:knife][:validation_key_url]).scheme == 'file'
707
+ URI(Chef::Config[:knife][:validation_key_url]).path
708
+ else
709
+ validation_key_tmpfile.path
710
+ end
711
+ end
712
+ end
713
+
714
+ def validation_key_tmpfile
715
+ @validation_key_tmpfile ||= Tempfile.new('validation_key')
716
+ end
717
+
718
+ def download_validation_key(tempfile)
719
+ Chef::Log.debug 'Downloading validation key ' \
720
+ "<#{Chef::Config[:knife][:validation_key_url]}> to file " \
721
+ "<#{tempfile}>"
722
+
723
+ case URI(Chef::Config[:knife][:validation_key_url]).scheme
724
+ when 's3'
725
+ File.open(tempfile, 'w') { |f| f.write(s3_validation_key) }
726
+ end
727
+ end
728
+
729
+ def s3_validation_key
730
+ @s3_validation_key ||= begin
731
+ Chef::Knife::S3Source.fetch(Chef::Config[:knife][:validation_key_url])
732
+ end
733
+ end
734
+
735
+ def s3_secret
736
+ @s3_secret ||= begin
737
+ return false unless locate_config_value(:s3_secret)
738
+ Chef::Knife::S3Source.fetch(locate_config_value(:s3_secret))
739
+ end
740
+ end
741
+
742
+ def bootstrap_common_params(bootstrap)
743
+ bootstrap.config[:run_list] = config[:run_list]
744
+ bootstrap.config[:policy_group] = locate_config_value(:policy_group)
745
+ bootstrap.config[:policy_name] = locate_config_value(:policy_name)
746
+ bootstrap.config[:bootstrap_version] = locate_config_value(:bootstrap_version)
747
+ bootstrap.config[:distro] = locate_config_value(:distro) || default_bootstrap_template
748
+ # setting bootstrap_template value to template_file for backward compatibility
749
+ bootstrap.config[:template_file] = locate_config_value(:template_file) || locate_config_value(:bootstrap_template)
750
+ bootstrap.config[:environment] = locate_config_value(:environment)
751
+ bootstrap.config[:prerelease] = config[:prerelease]
752
+ bootstrap.config[:first_boot_attributes] = locate_config_value(:first_boot_attributes)
753
+ bootstrap.config[:first_boot_attributes_from_file] = locate_config_value(:first_boot_attributes_from_file)
754
+ bootstrap.config[:encrypted_data_bag_secret] = s3_secret || locate_config_value(:secret)
755
+ bootstrap.config[:encrypted_data_bag_secret_file] = locate_config_value(:secret_file)
756
+ # retrieving the secret from S3 is unique to knife-ec2, so we need to set "command line secret" to the value fetched from S3
757
+ # When linux vm is spawned, the chef's secret option proc function sets the value "command line secret" and this value is used by
758
+ # chef's code to check if secret option is passed through command line or not
759
+ Chef::Knife::DataBagSecretOptions.set_cl_secret(s3_secret) if locate_config_value(:s3_secret)
760
+ bootstrap.config[:secret] = s3_secret || locate_config_value(:secret)
761
+ bootstrap.config[:secret_file] = locate_config_value(:secret_file)
762
+ bootstrap.config[:node_ssl_verify_mode] = locate_config_value(:node_ssl_verify_mode)
763
+ bootstrap.config[:node_verify_api_cert] = locate_config_value(:node_verify_api_cert)
764
+ bootstrap.config[:bootstrap_no_proxy] = locate_config_value(:bootstrap_no_proxy)
765
+ bootstrap.config[:bootstrap_url] = locate_config_value(:bootstrap_url)
766
+ bootstrap.config[:bootstrap_install_command] = locate_config_value(:bootstrap_install_command)
767
+ bootstrap.config[:bootstrap_wget_options] = locate_config_value(:bootstrap_wget_options)
768
+ bootstrap.config[:bootstrap_curl_options] = locate_config_value(:bootstrap_curl_options)
769
+ bootstrap.config[:bootstrap_vault_file] = locate_config_value(:bootstrap_vault_file)
770
+ bootstrap.config[:bootstrap_vault_json] = locate_config_value(:bootstrap_vault_json)
771
+ bootstrap.config[:bootstrap_vault_item] = locate_config_value(:bootstrap_vault_item)
772
+ bootstrap.config[:use_sudo_password] = locate_config_value(:use_sudo_password)
773
+ bootstrap.config[:yes] = locate_config_value(:yes)
774
+ # Modify global configuration state to ensure hint gets set by
775
+ # knife-bootstrap
776
+ Chef::Config[:knife][:hints] ||= {}
777
+ Chef::Config[:knife][:hints]["ec2"] ||= {}
778
+ bootstrap
779
+ end
780
+
781
+ def fetch_server_fqdn(ip_addr)
782
+ require 'resolv'
783
+ Resolv.getname(ip_addr)
784
+ end
785
+
786
+ def bootstrap_for_windows_node(server, fqdn)
787
+ if locate_config_value(:bootstrap_protocol) == 'winrm' || locate_config_value(:bootstrap_protocol) == nil
788
+ if locate_config_value(:kerberos_realm)
789
+ #Fetch AD/WINS based fqdn if any for Kerberos-based Auth
790
+ fqdn = locate_config_value(:fqdn) || fetch_server_fqdn(server.private_ip_address)
791
+ end
792
+ bootstrap = Chef::Knife::BootstrapWindowsWinrm.new
793
+ bootstrap.config[:winrm_user] = locate_config_value(:winrm_user)
794
+ bootstrap.config[:winrm_password] = windows_password
795
+ bootstrap.config[:winrm_transport] = locate_config_value(:winrm_transport)
796
+ bootstrap.config[:kerberos_keytab_file] = locate_config_value(:kerberos_keytab_file)
797
+ bootstrap.config[:kerberos_realm] = locate_config_value(:kerberos_realm)
798
+ bootstrap.config[:kerberos_service] = locate_config_value(:kerberos_service)
799
+ bootstrap.config[:ca_trust_file] = locate_config_value(:ca_trust_file)
800
+ bootstrap.config[:winrm_port] = locate_config_value(:winrm_port)
801
+ bootstrap.config[:auth_timeout] = locate_config_value(:auth_timeout)
802
+ bootstrap.config[:winrm_ssl_verify_mode] = locate_config_value(:winrm_ssl_verify_mode)
803
+ elsif locate_config_value(:bootstrap_protocol) == 'ssh'
804
+ bootstrap = Chef::Knife::BootstrapWindowsSsh.new
805
+ bootstrap.config[:ssh_user] = locate_config_value(:ssh_user)
806
+ bootstrap.config[:ssh_password] = locate_config_value(:ssh_password)
807
+ bootstrap.config[:ssh_port] = locate_config_value(:ssh_port)
808
+ bootstrap.config[:identity_file] = locate_config_value(:identity_file)
809
+ bootstrap.config[:no_host_key_verify] = locate_config_value(:no_host_key_verify)
810
+ bootstrap.config[:forward_agent] = locate_config_value(:forward_agent)
811
+ else
812
+ ui.error("Unsupported Bootstrapping Protocol. Supported : winrm, ssh")
813
+ exit 1
814
+ end
815
+ bootstrap.name_args = [fqdn]
816
+ bootstrap.config[:msi_url] = locate_config_value(:msi_url)
817
+ bootstrap.config[:install_as_service] = locate_config_value(:install_as_service)
818
+ bootstrap.config[:session_timeout] = locate_config_value(:session_timeout)
819
+ bootstrap.config[:tags] = config[:tags] if locate_config_value(:tag_node_in_chef)
820
+
821
+ if locate_config_value(:chef_node_name)
822
+ bootstrap.config[:chef_node_name] = evaluate_node_name(locate_config_value(:chef_node_name))
823
+ else
824
+ bootstrap.config[:chef_node_name] = server.id
825
+ end
826
+ bootstrap_common_params(bootstrap)
827
+ end
828
+
829
+ def bootstrap_for_linux_node(server,ssh_host)
830
+ bootstrap = Chef::Knife::Bootstrap.new
831
+ bootstrap.name_args = [ssh_host]
832
+ bootstrap.config[:ssh_user] = config[:ssh_user]
833
+ bootstrap.config[:ssh_password] = locate_config_value(:ssh_password)
834
+ bootstrap.config[:ssh_port] = config[:ssh_port]
835
+ bootstrap.config[:ssh_gateway] = config[:ssh_gateway]
836
+ bootstrap.config[:identity_file] = config[:identity_file]
837
+ bootstrap.config[:tags] = config[:tags] if locate_config_value(:tag_node_in_chef)
838
+
839
+ if locate_config_value(:chef_node_name)
840
+ bootstrap.config[:chef_node_name] = evaluate_node_name(locate_config_value(:chef_node_name))
841
+ else
842
+ bootstrap.config[:chef_node_name] = server.id
843
+ end
844
+ bootstrap.config[:use_sudo] = true unless config[:ssh_user] == 'root'
845
+ # may be needed for vpc_mode
846
+ bootstrap.config[:host_key_verify] = config[:host_key_verify]
847
+ bootstrap_common_params(bootstrap)
848
+ end
849
+
850
+ def vpc_mode?
851
+ # Amazon Virtual Private Cloud requires a subnet_id. If
852
+ # present, do a few things differently
853
+ !!locate_config_value(:subnet_id)
854
+ end
855
+
856
+ def ami
857
+ @ami ||= connection.images.get(locate_config_value(:image))
858
+ end
859
+
860
+ def validate!
861
+ if Chef::Config[:knife].keys.include? :aws_ssh_key_id
862
+ Chef::Config[:knife][:ssh_key_name] = Chef::Config[:knife][:aws_ssh_key_id] if !Chef::Config[:knife][:ssh_key_name]
863
+ Chef::Config[:knife].delete(:aws_ssh_key_id)
864
+ ui.warn("Use of aws_ssh_key_id option in knife.rb config is deprecated, use ssh_key_name option instead.")
865
+ end
866
+
867
+ super([:image, :ssh_key_name, :aws_access_key_id, :aws_secret_access_key])
868
+
869
+ validate_nics! if locate_config_value(:network_interfaces)
870
+
871
+ if ami.nil?
872
+ ui.error("You have not provided a valid image (AMI) value.")
873
+ exit 1
874
+ end
875
+
876
+ if vpc_mode? and !!config[:security_groups]
877
+ ui.error("You are using a VPC, security groups specified with '-G' are not allowed, specify one or more security group ids with '-g' instead.")
878
+ exit 1
879
+ end
880
+
881
+ if !vpc_mode? and !!config[:private_ip_address]
882
+ ui.error("You can only specify a private IP address if you are using VPC.")
883
+ exit 1
884
+ end
885
+
886
+ if config[:dedicated_instance] and !vpc_mode?
887
+ ui.error("You can only specify a Dedicated Instance if you are using VPC.")
888
+ exit 1
889
+ end
890
+
891
+ if !vpc_mode? and config[:associate_public_ip]
892
+ ui.error("--associate-public-ip option only applies to VPC instances, and you have not specified a subnet id.")
893
+ exit 1
894
+ end
895
+
896
+ if config[:associate_eip]
897
+ eips = connection.addresses.collect{|addr| addr if addr.domain == eip_scope}.compact
898
+
899
+ unless eips.detect{|addr| addr.public_ip == config[:associate_eip] && addr.server_id == nil}
900
+ ui.error("Elastic IP requested is not available.")
901
+ exit 1
902
+ end
903
+ end
904
+
905
+ if config[:ebs_provisioned_iops] and config[:ebs_volume_type] != 'io1'
906
+ ui.error("--provisioned-iops option is only supported for volume type of 'io1'")
907
+ exit 1
908
+ end
909
+
910
+ if config[:ebs_volume_type] == 'io1' and config[:ebs_provisioned_iops].nil?
911
+ ui.error("--provisioned-iops option is required when using volume type of 'io1'")
912
+ exit 1
913
+ end
914
+
915
+ if config[:ebs_volume_type] and ! %w(gp2 io1 standard).include?(config[:ebs_volume_type])
916
+ ui.error("--ebs-volume-type must be 'standard' or 'io1' or 'gp2'")
917
+ msg opt_parser
918
+ exit 1
919
+ end
920
+
921
+ if config[:security_groups] && config[:security_groups].class == String
922
+ ui.error("Invalid value type for knife[:security_groups] in knife configuration file (i.e knife.rb). Type should be array. e.g - knife[:security_groups] = ['sgroup1']")
923
+ exit 1
924
+ end
925
+
926
+ # Validation for security_group_ids passed through knife.rb. It will raise error if values are not provided in Array.
927
+ if locate_config_value(:security_group_ids) && locate_config_value(:security_group_ids).class == String
928
+ ui.error("Invalid value type for knife[:security_group_ids] in knife configuration file (i.e knife.rb). Type should be array. e.g - knife[:security_group_ids] = ['sgroup1']")
929
+ exit 1
930
+ end
931
+
932
+ if config[:classic_link_vpc_id].nil? ^ config[:classic_link_vpc_security_group_ids].nil?
933
+ ui.error("--classic-link-vpc-id and --classic-link-vpc-security-group-ids must be used together")
934
+ exit 1
935
+ end
936
+
937
+ if vpc_mode? and config[:classic_link_vpc_id]
938
+ ui.error("You can only use ClassicLink if you are not using a VPC")
939
+ exit 1
940
+ end
941
+
942
+ if locate_config_value(:ebs_encrypted)
943
+ error_message = ""
944
+ errors = []
945
+ # validation for flavor and ebs_encrypted
946
+ if !locate_config_value(:flavor)
947
+ ui.error("--ebs-encrypted option requires valid flavor to be specified.")
948
+ exit 1
949
+ elsif (locate_config_value(:ebs_encrypted) and ! %w(m3.medium m3.large m3.xlarge m3.2xlarge m4.large m4.xlarge
950
+ m4.2xlarge m4.4xlarge m4.10xlarge m4.16xlarge t2.nano t2.micro t2.small
951
+ t2.medium t2.large t2.xlarge t2.2xlarge d2.xlarge d2.2xlarge d2.4xlarge
952
+ d2.8xlarge c4.large c4.xlarge c4.2xlarge c4.4xlarge c4.8xlarge c3.large
953
+ c3.xlarge c3.2xlarge c3.4xlarge c3.8xlarge cr1.8xlarge r3.large r3.xlarge
954
+ r3.2xlarge r3.4xlarge r3.8xlarge r4.large r4.xlarge r4.2xlarge r4.4xlarge
955
+ r4.8xlarge r4.16xlarge x1.16xlarge x1.32xlarge i2.xlarge i2.2xlarge i2.4xlarge
956
+ i2.8xlarge i3.large i3.xlarge i3.2xlarge i3.4xlarge i3.8xlarge i3.16xlarge
957
+ f1.2xlarge f1.16xlarge g2.2xlarge g2.8xlarge p2.xlarge p2.8xlarge p2.16xlarge).include?(locate_config_value(:flavor)))
958
+ ui.error("--ebs-encrypted option is not supported for #{locate_config_value(:flavor)} flavor.")
959
+ exit 1
960
+ end
961
+
962
+ # validation for ebs_size and ebs_volume_type and ebs_encrypted
963
+ if !locate_config_value(:ebs_size)
964
+ errors << "--ebs-encrypted option requires valid --ebs-size to be specified."
965
+ elsif locate_config_value(:ebs_volume_type) == "gp2" and ! locate_config_value(:ebs_size).to_i.between?(1, 16384)
966
+ errors << "--ebs-size should be in between 1-16384 for 'gp2' ebs volume type."
967
+ elsif locate_config_value(:ebs_volume_type) == "io1" and ! locate_config_value(:ebs_size).to_i.between?(4, 16384)
968
+ errors << "--ebs-size should be in between 4-16384 for 'io1' ebs volume type."
969
+ elsif locate_config_value(:ebs_volume_type) == "standard" and ! locate_config_value(:ebs_size).to_i.between?(1, 1024)
970
+ errors << "--ebs-size should be in between 1-1024 for 'standard' ebs volume type."
971
+ end
972
+
973
+ if errors.each{|e| error_message = "#{error_message} #{e}"}.any?
974
+ ui.error(error_message)
975
+ exit 1
976
+ end
977
+ end
978
+
979
+ if locate_config_value(:spot_price) && locate_config_value(:disable_api_termination)
980
+ ui.error("spot-price and disable-api-termination options cannot be passed together as 'Termination Protection' cannot be enabled for spot instances.")
981
+ exit 1
982
+ end
983
+
984
+ if locate_config_value(:spot_price).nil? && locate_config_value(:spot_wait_mode).downcase != 'prompt'
985
+ ui.error('spot-wait-mode option requires that a spot-price option is set.')
986
+ exit 1
987
+ end
988
+
989
+ volume_tags = locate_config_value(:volume_tags)
990
+ if !volume_tags.nil? and volume_tags.length != volume_tags.to_s.count('=')
991
+ ui.error("Volume Tags should be entered in a key = value pair")
992
+ exit 1
993
+ end
994
+
995
+ if (locate_config_value(:winrm_password).to_s.length > 14 )
996
+ ui.warn("The password provided is longer than 14 characters. Computers with Windows prior to Windows 2000 will not be able to use this account. Do you want to continue this operation? (Y/N):")
997
+ password_promt = STDIN.gets.chomp.upcase
998
+ if (password_promt == "N")
999
+ raise "Exiting as operation with password greater than 14 characters not accepted"
1000
+ elsif (password_promt == "Y")
1001
+ @allow_long_password = "/yes"
1002
+ else
1003
+ raise "The input provided is incorrect."
1004
+ end
1005
+ end
1006
+
1007
+ end
1008
+
1009
+ def tags
1010
+ tags = locate_config_value(:tags)
1011
+ if !tags.nil? and tags.length != tags.to_s.count('=')
1012
+ ui.error("Tags should be entered in a key = value pair")
1013
+ exit 1
1014
+ end
1015
+ tags
1016
+ end
1017
+
1018
+ def eip_scope
1019
+ if vpc_mode?
1020
+ "vpc"
1021
+ else
1022
+ "standard"
1023
+ end
1024
+ end
1025
+
1026
+ def ssl_config_user_data
1027
+ user_related_commands = ""
1028
+ winrm_user = locate_config_value(:winrm_user).split("\\")
1029
+ if (winrm_user[0] == ".") || (winrm_user[0] == "") ||(winrm_user.length == 1)
1030
+ user_related_commands = <<-EOH
1031
+ net user /add #{locate_config_value(:winrm_user).delete('.\\')} #{windows_password} #{@allow_long_password};
1032
+ net localgroup Administrators /add #{locate_config_value(:winrm_user).delete('.\\')};
1033
+ EOH
1034
+ end
1035
+ <<-EOH
1036
+ #{user_related_commands}
1037
+ If (-Not (Get-Service WinRM | Where-Object {$_.status -eq "Running"})) {
1038
+ winrm quickconfig -q
1039
+ }
1040
+ If (winrm e winrm/config/listener | Select-String -Pattern " Transport = HTTP\\b" -Quiet) {
1041
+ winrm delete winrm/config/listener?Address=*+Transport=HTTP
1042
+ }
1043
+ $vm_name = invoke-restmethod -uri http://169.254.169.254/latest/meta-data/public-ipv4
1044
+ If (-Not $vm_name) {
1045
+ $vm_name = invoke-restmethod -uri http://169.254.169.254/latest/meta-data/local-ipv4
1046
+ }
1047
+
1048
+ $name = new-object -com "X509Enrollment.CX500DistinguishedName.1"
1049
+ $name.Encode("CN=$vm_name", 0)
1050
+ $key = new-object -com "X509Enrollment.CX509PrivateKey.1"
1051
+ $key.ProviderName = "Microsoft RSA SChannel Cryptographic Provider"
1052
+ $key.KeySpec = 1
1053
+ $key.Length = 2048
1054
+ $key.SecurityDescriptor = "D:PAI(A;;0xd01f01ff;;;SY)(A;;0xd01f01ff;;;BA)(A;;0x80120089;;;NS)"
1055
+ $key.MachineContext = 1
1056
+ $key.Create()
1057
+ $serverauthoid = new-object -com "X509Enrollment.CObjectId.1"
1058
+ $serverauthoid.InitializeFromValue("1.3.6.1.5.5.7.3.1")
1059
+ $ekuoids = new-object -com "X509Enrollment.CObjectIds.1"
1060
+ $ekuoids.add($serverauthoid)
1061
+ $ekuext = new-object -com "X509Enrollment.CX509ExtensionEnhancedKeyUsage.1"
1062
+ $ekuext.InitializeEncode($ekuoids)
1063
+ $cert = new-object -com "X509Enrollment.CX509CertificateRequestCertificate.1"
1064
+ $cert.InitializeFromPrivateKey(2, $key, "")
1065
+ $cert.Subject = $name
1066
+ $cert.Issuer = $cert.Subject
1067
+ $cert.NotBefore = get-date
1068
+ $cert.NotAfter = $cert.NotBefore.AddYears(10)
1069
+ $cert.X509Extensions.Add($ekuext)
1070
+ $cert.Encode()
1071
+ $enrollment = new-object -com "X509Enrollment.CX509Enrollment.1"
1072
+ $enrollment.InitializeFromRequest($cert)
1073
+ $certdata = $enrollment.CreateRequest(0)
1074
+ $enrollment.InstallResponse(2, $certdata, 0, "")
1075
+
1076
+ $thumbprint = (Get-ChildItem -Path cert:\\localmachine\\my | Where-Object {$_.Subject -match "$vm_name"}).Thumbprint;
1077
+ $create_listener_cmd = "winrm create winrm/config/Listener?Address=*+Transport=HTTPS '@{Hostname=`"$vm_name`";CertificateThumbprint=`"$thumbprint`"}'"
1078
+ iex $create_listener_cmd
1079
+ netsh advfirewall firewall add rule name="WinRM HTTPS" protocol=TCP dir=in Localport=5986 remoteport=any action=allow localip=any remoteip=any profile=any enable=yes
1080
+ EOH
1081
+ end
1082
+
1083
+ def ssl_config_data_already_exist?
1084
+ File.read(locate_config_value(:aws_user_data)).gsub(/\\\\/,"\\").include? ssl_config_user_data.strip
1085
+ end
1086
+
1087
+ def process_user_data(script_lines)
1088
+ if !ssl_config_data_already_exist?
1089
+ ps_start_tag = "<powershell>\n"
1090
+ ps_end_tag = "</powershell>\n"
1091
+ ps_start_tag_index = script_lines.index(ps_start_tag) || script_lines.index(ps_start_tag.strip)
1092
+ ps_end_tag_index = script_lines.index(ps_end_tag) || script_lines.index(ps_end_tag.strip)
1093
+ case
1094
+ when ( ps_start_tag_index && !ps_end_tag_index ) || ( !ps_start_tag_index && ps_end_tag_index )
1095
+ ui.error("Provided user_data file is invalid.")
1096
+ exit 1
1097
+ when ps_start_tag_index && ps_end_tag_index
1098
+ script_lines[ps_end_tag_index] = ssl_config_user_data + ps_end_tag
1099
+ when !ps_start_tag_index && !ps_end_tag_index
1100
+ script_lines.insert(-1,"\n\n" + ps_start_tag + ssl_config_user_data + ps_end_tag)
1101
+ end
1102
+ end
1103
+ script_lines
1104
+ end
1105
+
1106
+ def create_server_def
1107
+ server_def = {
1108
+ :image_id => locate_config_value(:image),
1109
+ :groups => config[:security_groups],
1110
+ :flavor_id => locate_config_value(:flavor),
1111
+ :key_name => locate_config_value(:ssh_key_name),
1112
+ :availability_zone => locate_config_value(:availability_zone),
1113
+ :price => locate_config_value(:spot_price),
1114
+ :request_type => locate_config_value(:spot_request_type)
1115
+ }
1116
+
1117
+ if primary_eni = locate_config_value(:primary_eni)
1118
+ server_def[:network_interfaces] = [
1119
+ {
1120
+ :NetworkInterfaceId => primary_eni,
1121
+ :DeviceIndex => "0"
1122
+ }
1123
+ ]
1124
+ else
1125
+ server_def[:security_group_ids] = locate_config_value(:security_group_ids)
1126
+ server_def[:subnet_id] = locate_config_value(:subnet_id) if vpc_mode?
1127
+ end
1128
+
1129
+ server_def[:private_ip_address] = locate_config_value(:private_ip_address) if vpc_mode?
1130
+ server_def[:placement_group] = locate_config_value(:placement_group)
1131
+ server_def[:iam_instance_profile_name] = locate_config_value(:iam_instance_profile)
1132
+ server_def[:tenancy] = "dedicated" if vpc_mode? and locate_config_value(:dedicated_instance)
1133
+ server_def[:associate_public_ip] = locate_config_value(:associate_public_ip) if vpc_mode? and config[:associate_public_ip]
1134
+
1135
+ if locate_config_value(:winrm_transport) == 'ssl'
1136
+ if locate_config_value(:aws_user_data)
1137
+ begin
1138
+ user_data = File.readlines(locate_config_value(:aws_user_data))
1139
+ if config[:create_ssl_listener]
1140
+ user_data = process_user_data(user_data)
1141
+ end
1142
+ user_data = user_data.join
1143
+ server_def.merge!(:user_data => user_data)
1144
+ rescue
1145
+ ui.warn("Cannot read #{locate_config_value(:aws_user_data)}: #{$!.inspect}. Ignoring option.")
1146
+ end
1147
+ else
1148
+ if config[:create_ssl_listener]
1149
+ server_def.merge!(:user_data => "<powershell>\n" + ssl_config_user_data + "</powershell>\n")
1150
+ end
1151
+ end
1152
+ else
1153
+ if locate_config_value(:aws_user_data)
1154
+ begin
1155
+ server_def.merge!(:user_data => File.read(locate_config_value(:aws_user_data)))
1156
+ rescue
1157
+ ui.warn("Cannot read #{locate_config_value(:aws_user_data)}: #{$!.inspect}. Ignoring option.")
1158
+ end
1159
+ end
1160
+ end
1161
+
1162
+ if config[:ebs_optimized]
1163
+ server_def[:ebs_optimized] = "true"
1164
+ else
1165
+ server_def[:ebs_optimized] = "false"
1166
+ end
1167
+
1168
+ if ami.root_device_type == "ebs"
1169
+ if locate_config_value(:ebs_encrypted)
1170
+ ami_map = ami.block_device_mapping[1]
1171
+ else
1172
+ ami_map = ami.block_device_mapping.first
1173
+ end
1174
+
1175
+ ebs_size = begin
1176
+ if config[:ebs_size]
1177
+ Integer(config[:ebs_size]).to_s
1178
+ else
1179
+ ami_map["volumeSize"].to_s
1180
+ end
1181
+ rescue ArgumentError
1182
+ puts "--ebs-size must be an integer"
1183
+ msg opt_parser
1184
+ exit 1
1185
+ end
1186
+ delete_term = if config[:ebs_no_delete_on_term]
1187
+ "false"
1188
+ else
1189
+ ami_map["deleteOnTermination"]
1190
+ end
1191
+ iops_rate = begin
1192
+ if config[:ebs_provisioned_iops]
1193
+ Integer(config[:ebs_provisioned_iops]).to_s
1194
+ else
1195
+ ami_map["iops"].to_s
1196
+ end
1197
+ rescue ArgumentError
1198
+ puts "--provisioned-iops must be an integer"
1199
+ msg opt_parser
1200
+ exit 1
1201
+ end
1202
+
1203
+ server_def[:block_device_mapping] =
1204
+ [{
1205
+ 'DeviceName' => ami_map["deviceName"],
1206
+ 'Ebs.VolumeSize' => ebs_size,
1207
+ 'Ebs.DeleteOnTermination' => delete_term,
1208
+ 'Ebs.VolumeType' => config[:ebs_volume_type],
1209
+ }]
1210
+ server_def[:block_device_mapping].first['Ebs.Iops'] = iops_rate unless iops_rate.empty?
1211
+ server_def[:block_device_mapping].first['Ebs.Encrypted'] = true if locate_config_value(:ebs_encrypted)
1212
+ end
1213
+
1214
+ (config[:ephemeral] || []).each_with_index do |device_name, i|
1215
+ server_def[:block_device_mapping] = (server_def[:block_device_mapping] || []) << {'VirtualName' => "ephemeral#{i}", 'DeviceName' => device_name}
1216
+ end
1217
+
1218
+ ## cannot pass disable_api_termination option to the API when using spot instances ##
1219
+ server_def[:disable_api_termination] = locate_config_value(:disable_api_termination) if locate_config_value(:spot_price).nil?
1220
+
1221
+ server_def[:instance_initiated_shutdown_behavior] = locate_config_value(:instance_initiated_shutdown_behavior)
1222
+
1223
+ server_def
1224
+ end
1225
+
1226
+ def wait_for_sshd(hostname)
1227
+ ssh_gateway = get_ssh_gateway_for(hostname)
1228
+ ssh_gateway ? wait_for_tunnelled_sshd(ssh_gateway, hostname) : wait_for_direct_sshd(hostname, config[:ssh_port])
1229
+ end
1230
+
1231
+ def get_ssh_gateway_for(hostname)
1232
+ if config[:ssh_gateway]
1233
+ # The ssh_gateway specified in the knife config (if any) takes
1234
+ # precedence over anything in the SSH configuration
1235
+ Chef::Log.debug("Using ssh gateway #{config[:ssh_gateway]} from knife config")
1236
+ config[:ssh_gateway]
1237
+ else
1238
+ # Next, check if the SSH configuration has a ProxyCommand
1239
+ # directive for this host. If there is one, parse out the
1240
+ # host from the proxy command
1241
+ ssh_proxy = Net::SSH::Config.for(hostname)[:proxy]
1242
+ if ssh_proxy.respond_to?(:command_line_template)
1243
+ # ssh gateway_hostname nc %h %p
1244
+ proxy_pattern = /ssh\s+(\S+)\s+nc/
1245
+ matchdata = proxy_pattern.match(ssh_proxy.command_line_template)
1246
+ if matchdata.nil?
1247
+ Chef::Log.debug("Unable to determine ssh gateway for '#{hostname}' from ssh config template: #{ssh_proxy.command_line_template}")
1248
+ nil
1249
+ else
1250
+ # Return hostname extracted from command line template
1251
+ Chef::Log.debug("Using ssh gateway #{matchdata[1]} from ssh config")
1252
+ matchdata[1]
1253
+ end
1254
+ else
1255
+ # Return nil if we cannot find an ssh_gateway
1256
+ Chef::Log.debug("No ssh gateway found, making a direct connection")
1257
+ nil
1258
+ end
1259
+ end
1260
+ end
1261
+
1262
+ def wait_for_tunnelled_sshd(ssh_gateway, hostname)
1263
+ initial = true
1264
+ print(".") until tunnel_test_ssh(ssh_gateway, hostname) {
1265
+ if initial
1266
+ initial = false
1267
+ sleep (vpc_mode? ? 40 : 10)
1268
+ else
1269
+ sleep 10
1270
+ end
1271
+ puts("done")
1272
+ }
1273
+ end
1274
+
1275
+ def tunnel_test_ssh(ssh_gateway, hostname, &block)
1276
+ status = false
1277
+ gateway = configure_ssh_gateway(ssh_gateway)
1278
+ gateway.open(hostname, config[:ssh_port]) do |local_tunnel_port|
1279
+ status = tcp_test_ssh('localhost', local_tunnel_port, &block)
1280
+ end
1281
+ status
1282
+ rescue SocketError, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ENETUNREACH, IOError
1283
+ sleep 2
1284
+ false
1285
+ rescue Errno::EPERM, Errno::ETIMEDOUT
1286
+ false
1287
+ end
1288
+
1289
+ def configure_ssh_gateway(ssh_gateway)
1290
+ gw_host, gw_user = ssh_gateway.split('@').reverse
1291
+ gw_host, gw_port = gw_host.split(':')
1292
+ gateway_options = { :port => gw_port || 22 }
1293
+
1294
+ # Load the SSH config for the SSH gateway host.
1295
+ # Set the gateway user if it was not part of the
1296
+ # SSH gateway string, and use any configured
1297
+ # SSH keys.
1298
+ ssh_gateway_config = Net::SSH::Config.for(gw_host)
1299
+ gw_user ||= ssh_gateway_config[:user]
1300
+
1301
+ # Always use the gateway keys from the SSH Config
1302
+ gateway_keys = ssh_gateway_config[:keys]
1303
+
1304
+ # Use the keys specificed on the command line if available (overrides SSH Config)
1305
+ if config[:ssh_gateway_identity]
1306
+ gateway_keys = Array(locate_config_value(:ssh_gateway_identity))
1307
+ end
1308
+
1309
+ unless gateway_keys.nil?
1310
+ gateway_options[:keys] = gateway_keys
1311
+ end
1312
+
1313
+ Net::SSH::Gateway.new(gw_host, gw_user, gateway_options)
1314
+ end
1315
+
1316
+ def wait_for_direct_sshd(hostname, ssh_port)
1317
+ initial = true
1318
+ print(".") until tcp_test_ssh(hostname, ssh_port) {
1319
+ if initial
1320
+ initial = false
1321
+ sleep (vpc_mode? ? 40 : 10)
1322
+ else
1323
+ sleep 10
1324
+ end
1325
+ puts("done")
1326
+ }
1327
+ end
1328
+
1329
+ def subnet_public_ip_on_launch?
1330
+ connection.subnets.get(server.subnet_id).map_public_ip_on_launch
1331
+ end
1332
+
1333
+ def ssh_connect_host
1334
+ unless @ssh_connect_host
1335
+ if config[:server_connect_attribute]
1336
+ connect_attribute = config[:server_connect_attribute]
1337
+ server.send(config[:server_connect_attribute])
1338
+ elsif vpc_mode? && !(subnet_public_ip_on_launch? || config[:associate_public_ip] || config[:associate_eip])
1339
+ connect_attribute = "private_ip_address"
1340
+ server.private_ip_address
1341
+ else
1342
+ connect_attribute = server.dns_name ? "dns_name" : "public_ip_address"
1343
+ server.send(connect_attribute)
1344
+ end
1345
+ @ssh_connect_host = server.send(connect_attribute)
1346
+ end
1347
+
1348
+ puts "\nSSH Target Address: #{@ssh_connect_host}(#{connect_attribute})"
1349
+ @ssh_connect_host
1350
+ end
1351
+
1352
+ def create_tags(hashed_tags)
1353
+ hashed_tags.each_pair do |key,val|
1354
+ connection.tags.create :key => key, :value => val, :resource_id => @server.id
1355
+ end
1356
+ end
1357
+
1358
+ def associate_eip(elastic_ip)
1359
+ connection.associate_address(server.id, elastic_ip.public_ip, nil, elastic_ip.allocation_id)
1360
+ @server.wait_for(locate_config_value(:aws_connection_timeout)) { public_ip_address == elastic_ip.public_ip }
1361
+ end
1362
+
1363
+ def validate_nics!
1364
+ valid_nic_ids = connection.network_interfaces.all(
1365
+ vpc_mode? ? { 'vpc-id' => vpc_id } : {}
1366
+ ).map(&:network_interface_id)
1367
+ invalid_nic_ids =
1368
+ locate_config_value(:network_interfaces) - valid_nic_ids
1369
+ return true if invalid_nic_ids.empty?
1370
+ ui.error 'The following network interfaces are invalid: ' \
1371
+ "#{invalid_nic_ids.join(', ')}"
1372
+ exit 1
1373
+ end
1374
+
1375
+ def vpc_id
1376
+ @vpc_id ||= begin
1377
+ connection.subnets.get(locate_config_value(:subnet_id)).vpc_id
1378
+ end
1379
+ end
1380
+
1381
+ def wait_for_nic_attachment
1382
+ attached_nics_count = 0
1383
+ until attached_nics_count ==
1384
+ locate_config_value(:network_interfaces).count
1385
+ attachment_nics =
1386
+ locate_config_value(:network_interfaces).map do |nic_id|
1387
+ connection.network_interfaces.get(nic_id).attachment['status']
1388
+ end
1389
+ attached_nics_count = attachment_nics.grep('attached').count
1390
+ end
1391
+ end
1392
+
1393
+ def attach_nics
1394
+ attachments = []
1395
+ config[:network_interfaces].each_with_index do |nic_id, index|
1396
+ attachments << connection.attach_network_interface(nic_id,
1397
+ server.id,
1398
+ index + 1).body
1399
+ end
1400
+ wait_for_nic_attachment
1401
+ # rubocop:disable Style/RedundantReturn
1402
+ return attachments
1403
+ # rubocop:enable Style/RedundantReturn
1404
+ end
1405
+
1406
+ def enable_classic_link(vpc_id, security_group_ids)
1407
+ connection.attach_classic_link_vpc(server.id, vpc_id, security_group_ids)
1408
+ end
1409
+
1410
+ def ssh_override_winrm
1411
+ # unchanged ssh_user and changed winrm_user, override ssh_user
1412
+ if locate_config_value(:ssh_user).eql?(options[:ssh_user][:default]) &&
1413
+ !locate_config_value(:winrm_user).eql?(options[:winrm_user][:default])
1414
+ config[:ssh_user] = locate_config_value(:winrm_user)
1415
+ end
1416
+ # unchanged ssh_port and changed winrm_port, override ssh_port
1417
+ if locate_config_value(:ssh_port).eql?(options[:ssh_port][:default]) &&
1418
+ !locate_config_value(:winrm_port).eql?(options[:winrm_port][:default])
1419
+ config[:ssh_port] = locate_config_value(:winrm_port)
1420
+ end
1421
+ # unset ssh_password and set winrm_password, override ssh_password
1422
+ if locate_config_value(:ssh_password).nil? &&
1423
+ !locate_config_value(:winrm_password).nil?
1424
+ config[:ssh_password] = locate_config_value(:winrm_password)
1425
+ end
1426
+ # unset identity_file and set kerberos_keytab_file, override identity_file
1427
+ if locate_config_value(:identity_file).nil? &&
1428
+ !locate_config_value(:kerberos_keytab_file).nil?
1429
+ config[:identity_file] = locate_config_value(:kerberos_keytab_file)
1430
+ end
1431
+ end
1432
+
1433
+ def tcp_test_winrm(ip_addr, port)
1434
+ tcp_socket = TCPSocket.new(ip_addr, port)
1435
+ yield
1436
+ true
1437
+ rescue SocketError
1438
+ sleep 2
1439
+ false
1440
+ rescue Errno::ETIMEDOUT
1441
+ false
1442
+ rescue Errno::EPERM
1443
+ false
1444
+ rescue Errno::ECONNREFUSED
1445
+ sleep 2
1446
+ false
1447
+ rescue Errno::EHOSTUNREACH
1448
+ sleep 2
1449
+ false
1450
+ rescue Errno::ENETUNREACH
1451
+ sleep 2
1452
+ false
1453
+ ensure
1454
+ tcp_socket && tcp_socket.close
1455
+ end
1456
+
1457
+ def tcp_test_ssh(hostname, ssh_port)
1458
+ tcp_socket = TCPSocket.new(hostname, ssh_port)
1459
+ readable = IO.select([tcp_socket], nil, nil, 5)
1460
+ if readable
1461
+ ssh_banner = tcp_socket.gets
1462
+ if ssh_banner.nil? or ssh_banner.empty?
1463
+ false
1464
+ else
1465
+ Chef::Log.debug("sshd accepting connections on #{hostname}, banner is #{ssh_banner}")
1466
+ yield
1467
+ true
1468
+ end
1469
+ else
1470
+ false
1471
+ end
1472
+ rescue SocketError, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ENETUNREACH, Errno::ENOTCONN, IOError
1473
+ Chef::Log.debug("ssh failed to connect: #{hostname}")
1474
+ sleep 2
1475
+ false
1476
+ rescue Errno::EPERM, Errno::ETIMEDOUT
1477
+ Chef::Log.debug("ssh timed out: #{hostname}")
1478
+ false
1479
+ # This happens on some mobile phone networks
1480
+ rescue Errno::ECONNRESET
1481
+ Chef::Log.debug("ssh reset its connection: #{hostname}")
1482
+ sleep 2
1483
+ false
1484
+ ensure
1485
+ tcp_socket && tcp_socket.close
1486
+ end
1487
+
1488
+ def decrypt_admin_password(encoded_password, key)
1489
+ require 'base64'
1490
+ require 'openssl'
1491
+ private_key = OpenSSL::PKey::RSA.new(key)
1492
+ encrypted_password = Base64.decode64(encoded_password)
1493
+ password = private_key.private_decrypt(encrypted_password)
1494
+ password
1495
+ end
1496
+
1497
+ def check_windows_password_available(server_id)
1498
+ sleep 10
1499
+ response = connection.get_password_data(server_id)
1500
+ if not response.body["passwordData"]
1501
+ return false
1502
+ end
1503
+ response.body["passwordData"]
1504
+ end
1505
+
1506
+ def windows_password
1507
+ if not locate_config_value(:winrm_password)
1508
+ if locate_config_value(:identity_file)
1509
+ print "\n#{ui.color("Waiting for Windows Admin password to be available", :magenta)}"
1510
+ print(".") until check_windows_password_available(@server.id) {
1511
+ puts("done")
1512
+ }
1513
+ response = connection.get_password_data(@server.id)
1514
+ data = File.read(locate_config_value(:identity_file))
1515
+ config[:winrm_password] = decrypt_admin_password(response.body["passwordData"], data)
1516
+ else
1517
+ ui.error("Cannot find SSH Identity file, required to fetch dynamically generated password")
1518
+ exit 1
1519
+ end
1520
+ else
1521
+ locate_config_value(:winrm_password)
1522
+ end
1523
+ end
1524
+
1525
+ def load_winrm_deps
1526
+ require 'winrm'
1527
+ require 'chef/knife/winrm'
1528
+ require 'chef/knife/bootstrap_windows_winrm'
1529
+ require 'chef/knife/bootstrap_windows_ssh'
1530
+ require 'chef/knife/core/windows_bootstrap_context'
1531
+ end
1532
+
1533
+ #Returns the name of node after evaluation of server id if %s is present.
1534
+ #Eg: "Test-%s" will return "Test-i-12345" in case the instance id is i-12345
1535
+ def evaluate_node_name(node_name)
1536
+ return node_name%server.id
1537
+ end
1538
+
1539
+ def create_volume_tags(hashed_volume_tags)
1540
+ hashed_volume_tags.each_pair do |key,val|
1541
+ connection.tags.create :key => key, :value => val, :resource_id => @server.block_device_mapping.first['volumeId']
1542
+ end
1543
+ end
1544
+
1545
+ end
1546
+ end
1547
+ end