ey-pro-cli 0.0.3 → 0.0.4

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: 9d3ac1354471718392e29083db65e0a4778490ce
4
- data.tar.gz: bb5d44ca7f4e4e40c7160e83585b550af339ba96
3
+ metadata.gz: 58c137b95089a3aad6cd5f491ce381e2e5c045b7
4
+ data.tar.gz: 3859a426ceebe15351e4bdfcdbd729b274631930
5
5
  SHA512:
6
- metadata.gz: 88947ac9c4683060f4195f67fe3460e996d0c019bbc671a9f1e9d7f39de64d4afd021a67115cb79c5dc2330e29345b40922c4cb241fe92e6963a48f219f79d54
7
- data.tar.gz: 58b3a3d2d5c4ec2e961a9a9983c8bda5bb5984f8d65a8d880928ccf8996149d4d59b62603139b2303da9a34c9c2c20d28e436e035431ba395b12ab90080c7505
6
+ metadata.gz: 3e1dcb49285ffd2d1861479cf36bd0e194f77af8d21d02b208b586001c13ab9bd0a41792d328d70af572184ae6408aa053e16982a08aa4980a76fdcb780acc33
7
+ data.tar.gz: 92d6ad641b39ef3a1b3170cd0e2759a585ea23171f82860c57703d07401b0fef25b1cd431b8f9e0848e64435e5a6e78eb138c778960e5bee42eecd03c44eeddf
@@ -0,0 +1,17 @@
1
+ language: ruby
2
+ sudo: false
3
+ rvm:
4
+ - 1.9.3
5
+ - 2.0.0
6
+ - 2.1.0
7
+ - ruby-head
8
+ notifications:
9
+ email: false
10
+ webhooks:
11
+ urls:
12
+ - https://ensemble.engineyard.com/travis
13
+ on_success: always
14
+ on_failure: always
15
+ on_start: true
16
+
17
+ script: bundle exec rake
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ey-pro-cli (0.0.2)
4
+ ey-pro-cli (0.0.3)
5
5
  addressable (~> 2.2)
6
6
  cistern (~> 0.11, >= 0.11.1)
7
7
  colorize
@@ -48,6 +48,7 @@ GEM
48
48
  faye-websocket (0.10.0)
49
49
  eventmachine (>= 0.12.0)
50
50
  websocket-driver (>= 0.5.1)
51
+ ffaker (2.1.0)
51
52
  http_parser.rb (0.6.0)
52
53
  method_source (0.8.2)
53
54
  mime-types (2.6.1)
@@ -60,6 +61,7 @@ GEM
60
61
  pry-nav (0.2.4)
61
62
  pry (>= 0.9.10, < 0.11.0)
62
63
  rack (1.6.4)
64
+ rake (10.4.2)
63
65
  rspec (3.3.0)
64
66
  rspec-core (~> 3.3.0)
65
67
  rspec-expectations (~> 3.3.0)
@@ -86,8 +88,10 @@ PLATFORMS
86
88
  DEPENDENCIES
87
89
  awesome_print
88
90
  ey-pro-cli!
91
+ ffaker
89
92
  pry
90
93
  pry-nav
94
+ rake
91
95
  rspec
92
96
 
93
97
  BUNDLED WITH
data/README.md CHANGED
@@ -1 +1,40 @@
1
1
  # ey-pro-cli
2
+
3
+ Command line interface for interacting with Engine Yard Pro. This gem is the spiritual successor to the [engineyard](https://github.com/engineyard/engineyard) gem.
4
+
5
+ ### Install
6
+
7
+ Install ey-pro-cli like any other ruby gem:
8
+
9
+ gem install ey-pro-cli
10
+
11
+ ### Login
12
+
13
+ If you do not have a `~/.ey-core` file with a valid API token, you will need to run `ey-pro login` in order to retrieve your API token. This command will ask for your Engine Yard email and password.
14
+
15
+
16
+ ### Commands
17
+
18
+ #### ey login
19
+
20
+ Retrieve your Engine Yard API token
21
+
22
+ #### ey logout
23
+
24
+ Delete your Engine Yard API token from the configuration file. Useful for switching accounts
25
+
26
+ #### ey deploy
27
+
28
+ Deploy your application to Engine Yard Pro.
29
+
30
+ ```
31
+ Options:
32
+ [--ref=REF] # A git reference to deploy
33
+ [--migrate=MIGRATE] # The migration command to run
34
+ -e, --environment=ENVIRONMENT # Name of the environment to deploy to
35
+ -a, [--account=ACCOUNT] # Name of the account that the environment resides in. If no account is specified, the app will deploy to the first environment that meets the criteria, in the accounts you have access to.
36
+ [--app=APP] # Application name to deploy. If :account is not specified, this will be the first app that matches the criteria in the accounts you have access to.
37
+ [--stream=STREAM] # Stream deploy output to console
38
+ [--no-migrate=NO-MIGRATE] # Do not run migration command
39
+ [--no-update-check=NO-UPDATE-CHECK] # Do not check for updates
40
+ ```
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+
4
+ namespace :spec do
5
+ task :mocked do
6
+ sh "bundle exec rspec spec/"
7
+ end
8
+ end
9
+
10
+ task :spec => ["spec:mocked"]
11
+
12
+ task default: "spec:mocked"
@@ -28,6 +28,9 @@ Gem::Specification.new do |s|
28
28
  s.add_development_dependency "awesome_print"
29
29
  s.add_development_dependency "pry"
30
30
  s.add_development_dependency "pry-nav"
31
+ s.add_development_dependency "rspec", "~> 3.0"
32
+ s.add_development_dependency "ffaker"
33
+ s.add_development_dependency "rake"
31
34
 
32
35
  #ey-core dependencies
33
36
  s.add_dependency "addressable", "~> 2.2"
@@ -1,5 +1,6 @@
1
1
  if ENV["TEST"] == 'true'
2
2
  require 'awesome_print'
3
+ require 'pry'
3
4
  end
4
5
 
5
6
  $:.unshift(File.dirname(__FILE__) + '/vendor/core')
@@ -10,15 +11,59 @@ require File.expand_path(File.dirname(__FILE__) + '/ey_pro_cli/version')
10
11
  require File.expand_path(File.dirname(__FILE__) + '/vendor/core/ey-core')
11
12
 
12
13
  class EyProCli < Thor
14
+ class << self
15
+ attr_accessor :core_file
16
+
17
+ def core_file
18
+ @core_file ||= File.expand_path("~/.ey-core")
19
+ end
20
+ end
21
+
22
+ desc "login", "Retrieve API token from Engine Yard Pro"
23
+
24
+ def login
25
+ email = ENV["EMAIL"] || ask("Email:")
26
+ password = ENV["PASSWORD"] || ask("Password:", echo: false)
27
+
28
+ token = unauthenticated_core_client.get_api_token(email, password).body["api_token"]
29
+
30
+ existing_token = core_yaml[core_url]
31
+ write_token = if existing_token && existing_token != token
32
+ puts "New token does not match existing token. Overwriting".yellow
33
+ true
34
+ elsif existing_token == token
35
+ puts "Token already exists".green
36
+ false
37
+ else
38
+ puts "Writing token".green
39
+ true
40
+ end
41
+ write_core_yaml(token) if write_token
42
+ rescue Ey::Core::Response::Unauthorized
43
+ abort "Invalid email or password".yellow
44
+ end
45
+
46
+ desc "logout", "Remove your Engine Yard Pro API token"
47
+
48
+ def logout
49
+ if core_yaml[core_url]
50
+ core_yaml.delete(core_url)
51
+ write_core_yaml
52
+ puts "Successfully removed API token from credentials file".green
53
+ else
54
+ puts "No API token found".yellow
55
+ end
56
+ end
57
+
13
58
  desc "deploy [OPTIONS]", "Deploy your EY Pro application"
14
59
  option :ref, desc: "A git reference to deploy"
15
60
  option :migrate, desc: "The migration command to run"
16
- option :environment, desc: "Name of the environment to deploy to", required: true
17
- option :account, desc: "Name of the account that the environment resides in. If no account is specified, the app will deploy to the first environment that meets the criteria, in the accounts you have access to."
61
+ option :environment, desc: "Name of the environment to deploy to", required: true, aliases: "-e"
62
+ option :account, desc: "Name of the account that the environment resides in. If no account is specified, the app will deploy to the first environment that meets the criteria, in the accounts you have access to.", aliases: "-a"
18
63
  option :app, desc: "Application name to deploy. If :account is not specified, this will be the first app that matches the criteria in the accounts you have access to."
19
- option :stream, desc: "Stream deploy output to console", type: :boolean
64
+ option :stream, desc: "Stream deploy output to console"
20
65
  option "no-migrate", desc: "Do not run migration command"
21
- option "no-update-check", desc: "Do not check for updates", type: :boolean
66
+ option "no-update-check", desc: "Do not check for updates"
22
67
 
23
68
  def deploy
24
69
  check_for_updates unless options["no-update-check"]
@@ -46,6 +91,20 @@ class EyProCli < Thor
46
91
  end
47
92
  end
48
93
 
94
+ no_commands do
95
+ def unauthenticated_core_client
96
+ @unauthenticated_core_client ||= Ey::Core::Client.new(token: nil, url: core_url)
97
+ end
98
+
99
+ def core_client
100
+ @core_client ||= Ey::Core::Client.new(url: core_url, config_file: self.class.core_file)
101
+ end
102
+
103
+ def core_url
104
+ (ENV["CORE_URL"] && File.join(ENV["CORE_URL"], '/')) || "https://api.engineyard.com/"
105
+ end
106
+ end
107
+
49
108
  private
50
109
 
51
110
  def check_for_updates
@@ -65,9 +124,13 @@ Current Version: #{current_version}
65
124
  end
66
125
  end
67
126
 
68
- def core_client
69
- url = ENV["CORE_URL"] || "https://api.engineyard.com"
70
- @core_client ||= Ey::Core::Client.new(url: url)
127
+ def write_core_yaml(token=nil)
128
+ core_yaml[core_url] = token if token
129
+ File.open(self.class.core_file, "w") { |f| f.puts core_yaml.to_yaml }
130
+ end
131
+
132
+ def core_yaml
133
+ @core_yaml ||= YAML.load_file(self.class.core_file) || {}
71
134
  end
72
135
 
73
136
  def core_account_for(options={})
@@ -1,3 +1,3 @@
1
1
  module EyProCliVersion
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.4"
3
3
  end
@@ -3,16 +3,16 @@ class Ey::Core::Client < Cistern::Service
3
3
  model_path "ey-core/models"
4
4
  request_path "ey-core/requests"
5
5
 
6
- collection :accounts
7
6
  collection :account_cancellations
8
7
  collection :account_referrals
9
- collection :addons
8
+ collection :accounts
10
9
  collection :addon_attachments
10
+ collection :addons
11
11
  collection :addresses
12
12
  collection :alerts
13
- collection :applications
14
13
  collection :application_archives
15
14
  collection :application_deployments
15
+ collection :applications
16
16
  collection :backup_files
17
17
  collection :backups
18
18
  collection :cluster_components
@@ -21,39 +21,40 @@ class Ey::Core::Client < Cistern::Service
21
21
  collection :component_actions
22
22
  collection :components
23
23
  collection :connectors
24
+ collection :contacts
24
25
  collection :costs
26
+ collection :database_plan_usages
25
27
  collection :database_server_revisions
26
28
  collection :database_server_snapshots
29
+ collection :database_server_usages
27
30
  collection :database_servers
28
31
  collection :database_services
29
- collection :database_server_usages
30
- collection :database_plan_usages
31
- collection :environments
32
32
  collection :environment_plan_usages
33
- collection :firewalls
34
- collection :firewall_rules
33
+ collection :environments
35
34
  collection :features
35
+ collection :firewall_rules
36
+ collection :firewalls
36
37
  collection :gems
37
- collection :keypairs
38
38
  collection :keypair_deployments
39
+ collection :keypairs
39
40
  collection :legacy_alerts
40
- collection :load_balancers
41
- collection :load_balancer_services
42
41
  collection :load_balancer_nodes
42
+ collection :load_balancer_services
43
+ collection :load_balancers
43
44
  collection :logical_databases
44
45
  collection :logs
45
46
  collection :memberships
46
47
  collection :messages
47
- collection :contacts
48
48
  collection :plan_usages
49
- collection :providers
50
49
  collection :provider_locations
50
+ collection :providers
51
51
  collection :requests
52
- collection :servers
53
52
  collection :server_events
54
53
  collection :server_usages
55
- collection :slots
54
+ collection :servers
55
+ collection :services
56
56
  collection :slot_components
57
+ collection :slots
57
58
  collection :ssl_certificates
58
59
  collection :storage_users
59
60
  collection :storages
@@ -64,33 +65,33 @@ class Ey::Core::Client < Cistern::Service
64
65
  collection :volumes
65
66
 
66
67
  model :account
68
+ model :account_cancellation
67
69
  model :account_referral
68
70
  model :account_trial
69
- model :alert
70
- model :billing
71
- model :account_cancellation
72
71
  model :addon
73
72
  model :addon_attachment
74
73
  model :address
75
- model :legacy_alert
74
+ model :alert
76
75
  model :application
77
76
  model :application_archive
78
77
  model :application_deployment
79
78
  model :backup
80
79
  model :backup_file
80
+ model :billing
81
81
  model :cluster
82
82
  model :cluster_component
83
83
  model :cluster_update
84
84
  model :component
85
85
  model :component_action
86
86
  model :connector
87
+ model :contact
87
88
  model :cost
89
+ model :database_plan_usage
88
90
  model :database_server
89
- model :database_server_snapshot
90
91
  model :database_server_revision
91
- model :database_service
92
+ model :database_server_snapshot
92
93
  model :database_server_usage
93
- model :database_plan_usage
94
+ model :database_service
94
95
  model :environment
95
96
  model :environment_plan_usage
96
97
  model :feature
@@ -99,15 +100,14 @@ class Ey::Core::Client < Cistern::Service
99
100
  model :gem
100
101
  model :keypair
101
102
  model :keypair_deployment
103
+ model :legacy_alert
102
104
  model :load_balancer
103
- model :load_balancer_service
104
105
  model :load_balancer_node
106
+ model :load_balancer_service
105
107
  model :log
106
108
  model :logical_database
107
109
  model :membership
108
110
  model :message
109
- model :metadata
110
- model :contact
111
111
  model :plan_usage
112
112
  model :provider
113
113
  model :provider_location
@@ -115,6 +115,7 @@ class Ey::Core::Client < Cistern::Service
115
115
  model :server
116
116
  model :server_event
117
117
  model :server_usage
118
+ model :service
118
119
  model :slot
119
120
  model :slot_component
120
121
  model :ssl_certificate
@@ -144,6 +145,7 @@ class Ey::Core::Client < Cistern::Service
144
145
  request :create_connector
145
146
  request :create_database_server
146
147
  request :create_database_service
148
+ request :create_database_service_snapshot
147
149
  request :create_environment
148
150
  request :create_firewall
149
151
  request :create_firewall_rule
@@ -200,6 +202,7 @@ class Ey::Core::Client < Cistern::Service
200
202
  request :get_alert
201
203
  request :get_alerting_environments
202
204
  request :get_alerts
205
+ request :get_api_token
203
206
  request :get_application
204
207
  request :get_application_archive
205
208
  request :get_application_archives
@@ -320,14 +323,18 @@ class Ey::Core::Client < Cistern::Service
320
323
  request :update_untracked_server
321
324
  request :upload_file
322
325
 
323
- recognizes :token, :url, :logger, :adapter, :builder, :connection_options, :auth_id, :auth_key, :cache
326
+ recognizes :token, :url, :logger, :adapter, :builder, :connection_options, :auth_id, :auth_key, :cache, :config_file
324
327
 
325
328
  module Shared
326
329
  attr_reader :authentication, :url, :cache
327
330
 
328
331
  def setup(options)
329
332
  token_dotfile = begin
330
- YAML.load_file(File.expand_path("~/.ey-core"))
333
+ if options[:config_file]
334
+ YAML.load_file(options[:config_file]) || {} # if the file is empty, yaml returns false
335
+ else
336
+ YAML.load_file(File.expand_path("~/.ey-core"))
337
+ end
331
338
  rescue Errno::ENOENT
332
339
  {}
333
340
  end
@@ -338,19 +345,27 @@ class Ey::Core::Client < Cistern::Service
338
345
  Ey::Core::MemoryCache
339
346
  else options[:cache]
340
347
  end
341
- @token = options[:token] || token_dotfile[@url] || token_dotfile[@url.gsub(/\/$/, "")] # matching with or without trailing slash
348
+
349
+ @authentication = nil
350
+ @token = if options.has_key?(:token) && options[:token].nil?
351
+ @authentication = :none
352
+ else
353
+ options[:token] || token_dotfile[@url] || token_dotfile[@url.gsub(/\/$/, "")] # matching with or without trailing slash
354
+ end
342
355
 
343
356
  # For HMAC
344
357
  @auth_id = options[:auth_id]
345
358
  @auth_key = options[:auth_key]
346
359
 
347
- if !@auth_id && !@auth_key && !@token
348
- raise "Missing token. Use Ey::Core::Client.new(token: mytoken) or add \"'#{@url}': mytoken\" to your ~/.ey-core file" unless @token
349
- elsif options[:token] || (@token && !@auth_id) # token was explicitly provided
350
- @authentication = :token
351
- else
352
- @authentication = :hmac
353
- @token = nil
360
+ unless @authentication == :none
361
+ if !@auth_id && !@auth_key && !@token
362
+ raise "Missing token. Use Ey::Core::Client.new(token: mytoken) or add \"'#{@url}': mytoken\" to your ~/.ey-core file" unless @token
363
+ elsif options[:token] || (@token && !@auth_id) # token was explicitly provided
364
+ @authentication = :token
365
+ else
366
+ @authentication = :hmac
367
+ @token = nil
368
+ end
354
369
  end
355
370
 
356
371
  @logger = options[:logger] || Logger.new(nil)
@@ -411,6 +426,12 @@ class Ey::Core::Client < Cistern::Service
411
426
  def url_for(path)
412
427
  File.join(@url.to_s, path.to_s)
413
428
  end
429
+
430
+ # @deprecated will be removed in 3.x
431
+ # @see {#services}
432
+ def metadata
433
+ services.get("core")
434
+ end
414
435
  end
415
436
 
416
437
  class Real
@@ -438,10 +459,12 @@ class Ey::Core::Client < Cistern::Service
438
459
  :interval_randomness => 0.05,
439
460
  :backoff_factor => 2
440
461
 
441
- if @token
442
- builder.use Ey::Core::TokenAuthentication, @token
443
- else
444
- builder.use :hmac, @auth_id, @auth_key
462
+ unless @authentication == :none
463
+ if @token
464
+ builder.use Ey::Core::TokenAuthentication, @token
465
+ else
466
+ builder.use :hmac, @auth_id, @auth_key
467
+ end
445
468
  end
446
469
 
447
470
  builder.use Ey::Core::Logger, @logger
@@ -590,8 +613,23 @@ class Ey::Core::Client < Cistern::Service
590
613
  }
591
614
  }
592
615
  possible_provider_locations = {
593
- "azure" => [{"id"=>"East US", "name"=>"East US"}, {"id"=>"North Europe", "name"=>"North Europe"}, {"id"=>"West Europe", "name"=>"West Europe"}, {"id"=>"West US", "name"=>"West US"}, {"id"=>"Japan East", "name"=>"Japan East"}],
594
- "aws" => [{"id"=>"us-east-1", "name"=>"Eastern United States"}, {"id"=>"us-west-1", "name"=>"Western US (Northern CA)"}, {"id"=>"us-west-2", "name"=>"Western US (Oregon)"}, {"id"=>"sa-east-1", "name"=>"South America"}, {"id"=>"eu-west-1", "name"=>"Europe"}, {"id"=>"ap-southeast-1", "name"=>"Singapore"}, {"id"=>"ap-southeast-2", "name"=>"Australia"}, {"id"=>"ap-northeast-1", "name"=>"Japan"}]
616
+ "azure" => [
617
+ { "id" => "East US", "name" => "East US" },
618
+ { "id" => "North Europe", "name" => "North Europe" },
619
+ { "id" => "West Europe", "name" => "West Europe" },
620
+ { "id" => "West US", "name" => "West US" },
621
+ { "id" => "Japan East", "name" => "Japan East" }
622
+ ],
623
+ "aws" => [
624
+ { "id" => "us-east-1", "name" => "Eastern United States" },
625
+ { "id" => "us-west-1", "name" => "Western US (Northern CA)" },
626
+ { "id" => "us-west-2", "name" => "Western US (Oregon)" },
627
+ { "id" => "sa-east-1", "name" => "South America" },
628
+ { "id" => "eu-west-1", "name" => "Europe" },
629
+ { "id" => "ap-southeast-1", "name" => "Singapore" },
630
+ { "id" => "ap-southeast-2", "name" => "Australia" },
631
+ { "id" => "ap-northeast-1", "name" => "Japan" }
632
+ ]
595
633
  }
596
634
  {
597
635
  :accounts => {},
@@ -0,0 +1,14 @@
1
+ class Ey::Core::Client::Services < Cistern::Collection
2
+
3
+ model Ey::Core::Client::Service
4
+
5
+ def all(options={})
6
+ connection.get_metadata.body.map { |name, attr|
7
+ new(attr.merge("name" => name))
8
+ }
9
+ end
10
+
11
+ def get(name)
12
+ self.all.find { |s| s.name == name }
13
+ end
14
+ end
@@ -3,7 +3,7 @@ module Ey::Core::Mock
3
3
  def create_database_service_resource(params)
4
4
  database_service = params["database_service"]
5
5
  logical_database = params["logical_database"]
6
- database_service_id = database_service["id"]
6
+ database_service_id = database_service.fetch("id")
7
7
  database_server = params["database_server"]
8
8
  provider_id = params["provider_id"]
9
9
 
@@ -30,7 +30,6 @@ module Ey::Core::Mock
30
30
  "deleted_at" => nil,
31
31
  )
32
32
 
33
-
34
33
  engine, location, _ = require_parameters(database_server, "engine", "location", "flavor", "version")
35
34
  database_server_id = self.uuid
36
35
 
@@ -40,7 +39,6 @@ module Ey::Core::Mock
40
39
  else 9922
41
40
  end
42
41
 
43
-
44
42
  database_server.merge!(
45
43
  "id" => database_server_id,
46
44
  "provisioned_id" => "#{database_service["name"].downcase}master",
@@ -71,9 +69,10 @@ module Ey::Core::Mock
71
69
  "location" => location.gsub(/[a-z]$/, ""),
72
70
  "rules" => url_for("/firewalls/#{firewall_id}/rules")
73
71
  }
72
+
74
73
  self.data[:database_server_firewalls] << [database_server_id, firewall_id]
75
- self.data[:database_servers][database_server.fetch("id")] = database_server
76
- self.data[:database_services][database_service.fetch("id")] = database_service
74
+ self.data[:database_servers][database_server_id] = database_server
75
+ self.data[:database_services][database_service_id] = database_service
77
76
 
78
77
  contacts.each { |contact|
79
78
  self.data[:contacts][contact.fetch("id")] = contact
@@ -84,6 +83,8 @@ module Ey::Core::Mock
84
83
  self.data[:logical_databases][logical_database.fetch("id")] = logical_database
85
84
  end
86
85
 
86
+ database_server.delete("resource_url")
87
+ database_service.delete("resource_url")
87
88
  r.delete("resource_url")
88
89
  end
89
90
  end
@@ -15,5 +15,9 @@ module Ey::Core::Mock
15
15
  def ip_address
16
16
  Array.new(4){rand(256)}.join('.')
17
17
  end
18
+
19
+ def api_token
20
+ SecureRandom.hex(20)
21
+ end
18
22
  end
19
23
  end
@@ -3,14 +3,35 @@ class Ey::Core::Client::DatabaseServerSnapshot < Ey::Core::Model
3
3
 
4
4
  identity :id
5
5
 
6
- attribute :location
6
+ attribute :deleted_at, type: :time
7
7
  attribute :engine
8
8
  attribute :engine_version
9
- attribute :storage, type: :integer
10
- attribute :provisioned_id
11
- attribute :deleted_at, type: :time
9
+ attribute :location
12
10
  attribute :provisioned_at, type: :time
11
+ attribute :provisioned_id
12
+ attribute :storage, type: :integer
13
13
 
14
14
  has_one :provider
15
15
  has_one :database_server
16
+ has_one :database_service
17
+
18
+ attr_accessor :name
19
+
20
+ def save
21
+ if new_record?
22
+ create_params = if collection.url
23
+ { "url" => collection.url }
24
+ elsif self.database_service
25
+ { "database_service" => self.database_service.identity }
26
+ else
27
+ requires_one :database_server, :database_service
28
+
29
+ { "database_service" => self.database_server.database_service.identity }
30
+ end
31
+
32
+ connection.requests.new(
33
+ connection.create_database_service_snapshot(create_params.merge("name" => self.name)).body["request"]
34
+ )
35
+ end
36
+ end
16
37
  end
@@ -51,6 +51,8 @@ class Ey::Core::Client::Request < Ey::Core::Model
51
51
  connection.providers.get!(id).database_server_snapshots
52
52
  elsif collection == "database-servers"
53
53
  connection.database_servers.get!(id).snapshots
54
+ else
55
+ connection.database_server_snapshots.get!(resource_id)
54
56
  end
55
57
  when /database_server/
56
58
  self.connection.database_servers.get!(resource_id)
@@ -0,0 +1,10 @@
1
+ class Ey::Core::Client::Service < Cistern::Model
2
+
3
+ identity :name
4
+
5
+ attribute :now, type: :time
6
+ attribute :environment
7
+ attribute :revision
8
+ attribute :version
9
+
10
+ end
@@ -24,7 +24,7 @@ class Ey::Core::Client
24
24
 
25
25
  if database_service.is_a?(Hash)
26
26
  require_parameters(params["database_service"], "name")
27
- database_service["id"] = self.serial_id
27
+ database_service["id"] = self.uuid
28
28
  else
29
29
  database_service = find(:database_services, database_service)
30
30
  existing_database_service = true
@@ -20,7 +20,7 @@ class Ey::Core::Client
20
20
 
21
21
  require_parameters(params["database_service"], "name")
22
22
  request_id = self.uuid
23
- resource_id = self.serial_id
23
+ resource_id = self.uuid
24
24
 
25
25
  database_service = params["database_service"].dup
26
26
  database_service["id"] = resource_id
@@ -0,0 +1,69 @@
1
+ class Ey::Core::Client
2
+ class Real
3
+ def create_database_service_snapshot(params={})
4
+ (url = params.delete("url")) || (name = params.fetch("name"))
5
+
6
+ request(
7
+ :method => :post,
8
+ :path => "/database-services/#{name}/snapshot",
9
+ :url => url,
10
+ :body => params,
11
+ )
12
+ end
13
+ end
14
+
15
+ class Mock
16
+ def create_database_service_snapshot(_params={})
17
+ params = Cistern::Hash.stringify_keys(_params)
18
+
19
+ server_or_service_id = resource_identity(params["url"]) || require_parameters(params, "database_service")
20
+
21
+ database_service = if (database_server = self.data[:database_servers][server_or_service_id])
22
+ find(:database_services, resource_identity(database_server["database_service"]))
23
+ else
24
+ find(:database_services, database_service_id)
25
+ end
26
+
27
+ database_server ||= self.data[:database_servers].values.find { |ds| url_for("/database-services/#{database_service["id"]}") == ds["database_service"] }
28
+ database_service_id = database_service["id"]
29
+
30
+ find(:database_services, database_service_id)
31
+
32
+ request_id = self.uuid
33
+ resource_id = self.uuid
34
+
35
+ resource = {
36
+ "id" => resource_id,
37
+ "created_at" => Time.now.to_s,
38
+ "updated_at" => Time.now.to_s,
39
+ "resource_url" => url_for("/database-server-snapshots/#{resource_id}"),
40
+ "provisioned_id" => params["name"] || "rds:#{database_server["provisioned_id"]}-#{Time.now.strftime("%Y-%M-%D-%H")}",
41
+ "database_server" => url_for("/database-servers/#{database_server["id"]}"),
42
+ "database_service" => url_for("/database-services/#{database_service_id}"),
43
+ "provider" => database_service["provider"],
44
+ "provisioned_at" => Time.now.to_s,
45
+ "logical_databases" => url_for("database-server-snapshots/#{resource_id}/logical-databases"),
46
+ }.merge(Cistern::Hash.slice(database_server, "storage", "engine", "engine_version"))
47
+
48
+ request = {
49
+ "id" => request_id,
50
+ "type" => "provision_database_server_snapshot",
51
+ "successful" => "true",
52
+ "started_at" => Time.now,
53
+ "finished_at" => nil,
54
+ "resource_url" => url_for("/database-server-snapshots/#{resource_id}"),
55
+ "resource" => [:database_server_snapshots, resource_id, resource],
56
+ }
57
+
58
+ self.data[:requests][request_id] = request
59
+
60
+ response_hash = request.dup
61
+ response_hash.delete("resource")
62
+
63
+ response(
64
+ :body => {"request" => response_hash},
65
+ :status => 201,
66
+ )
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,19 @@
1
+ class Ey::Core::Client
2
+ class Real
3
+ def get_api_token(username, password)
4
+ request(
5
+ :path => "tokens",
6
+ :method => :post,
7
+ :body => {email: username, password: password},
8
+ )
9
+ end
10
+ end
11
+
12
+ class Mock
13
+ def get_api_token(username, password)
14
+ response(
15
+ :body => {"api_token" => self.api_token}
16
+ )
17
+ end
18
+ end
19
+ end
@@ -1,26 +1,22 @@
1
1
  class Ey::Core::Client
2
2
  class Real
3
3
  def get_metadata(params={})
4
- url = params["url"]
5
-
6
- request(
7
- :path => "metadata",
8
- :url => url,
9
- )
4
+ request(path: "metadata")
10
5
  end
11
6
  end # Real
12
7
 
13
8
  class Mock
14
9
  def get_metadata(params={})
15
10
  response(
16
- :body => {
17
- environment: "mock",
18
- revision: "mock",
19
- version: "mock",
20
- now: Time.now
11
+ :body => {
12
+ "core" => {
13
+ "environment" => "mock",
14
+ "revision" => "mock",
15
+ "version" => "mock",
16
+ "now" => Time.now,
17
+ }
21
18
  },
22
- :status => 200,
23
- )
19
+ )
24
20
  end
25
21
  end # Mock
26
22
  end # Ey::Core::Client
@@ -1,5 +1,5 @@
1
1
  module Ey
2
2
  module Core
3
- VERSION = "2.7.3"
3
+ VERSION = "2.7.6"
4
4
  end
5
5
  end
@@ -0,0 +1,57 @@
1
+ require 'spec_helper'
2
+
3
+ describe EyProCli do
4
+ describe "deploy" do
5
+ before(:each) do
6
+ @tempfile.write <<-EOF
7
+ ---
8
+ '#{subject.core_url}': #{SecureRandom.hex(20)}'
9
+ EOF
10
+ @tempfile.rewind
11
+ subject.class.core_file = @tempfile
12
+ end
13
+
14
+ let(:options) { {} }
15
+
16
+ context "with a running environment" do
17
+ before(:each) do
18
+ account, environment, application = boot_environment(subject.core_client)
19
+
20
+ options.merge!(
21
+ :account => account.name,
22
+ :app => application.name,
23
+ :environment => environment.name,
24
+ )
25
+ end
26
+
27
+ it "successfully deploys" do
28
+ subject.options = options
29
+
30
+ expect(capture(:stdout) { subject.deploy }).to match(/deploy successful/i)
31
+ end
32
+
33
+ context "--stream" do
34
+ before(:each) { options.merge!(stream: true) }
35
+
36
+ it "streams" do
37
+ subject.options = options
38
+ expect(capture(:stdout) { subject.deploy }).to match(/subscription failed/i) # proves that the flag works, even if the mock doesn't
39
+ end
40
+ end
41
+
42
+ context "--no-update-check" do
43
+ before(:each) { subject.options = options }
44
+
45
+ it "has the updater message without the flag" do
46
+ expect(capture(:stdout) { subject.deploy }).to match(/gem outdated, consider updating/i)
47
+ end
48
+
49
+ it "does not have the message with the flag" do
50
+ options.merge!("no-update-check" => true)
51
+
52
+ expect(capture(:stdout) { subject.deploy }).not_to match(/gem outdated, consider updating/i)
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+
3
+ describe EyProCli do
4
+ describe "login" do
5
+ context "with a valid user and password" do
6
+ before(:each) { subject.class.core_file = @tempfile }
7
+
8
+ it "writes the token" do
9
+ allow(subject).to receive(:ask) { FFaker::Internet.email }
10
+
11
+ output = capture(:stdout) { subject.login }
12
+ expect(output).to match(/writing token/i)
13
+
14
+ expect(read_yaml(@tempfile)).to have_key("https://api.engineyard.com/")
15
+ end
16
+
17
+ it "updates the token if it does not match" do
18
+ allow(subject).to receive(:ask) { FFaker::Internet.email }
19
+
20
+ output = capture(:stdout) { subject.login }
21
+ expect(output).to match(/writing token/i)
22
+
23
+ output = capture(:stdout) { subject.login }
24
+ expect(output).to match(/token does not match/i)
25
+ end
26
+
27
+ it "does not update the token if it already exists" do
28
+ allow(subject).to receive(:ask) { FFaker::Internet.email }
29
+
30
+ output = capture(:stdout) { subject.login }
31
+ expect(output).to match(/writing token/i)
32
+
33
+ yaml = read_yaml(@tempfile)
34
+ expect(yaml).to have_key("https://api.engineyard.com/")
35
+
36
+ allow(subject.unauthenticated_core_client).to receive(:get_api_token) {
37
+ Ey::Core::Response.new(
38
+ :body => {"api_token" => yaml["https://api.engineyard.com/"]}
39
+ )
40
+ }
41
+
42
+ output = capture(:stdout) { subject.login }
43
+ expect(output).to match(/token already exists/i)
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ describe EyProCli do
4
+ describe "logout" do
5
+ context "with a token" do
6
+ before(:each) do
7
+ subject.class.core_file = @tempfile
8
+
9
+ allow(subject).to receive(:ask) { FFaker::Internet.email }
10
+
11
+ expect(capture(:stdout) { subject.login }).to match(/writing token/i)
12
+ end
13
+
14
+ it "removes the token" do
15
+ expect(capture(:stdout) { subject.logout }).to match(/successfully removed api token/i)
16
+
17
+ expect(read_yaml(@tempfile)).not_to have_key("https://api.engineyard.com/")
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,37 @@
1
+ require 'pry'
2
+ require 'tempfile'
3
+ require 'awesome_print'
4
+ require 'ffaker'
5
+ require 'securerandom'
6
+
7
+ ENV["MOCK_CLI"] ||= 'true'
8
+
9
+ def capture(stream)
10
+ begin
11
+ stream = stream.to_s
12
+ eval "$#{stream} = StringIO.new"
13
+ yield
14
+ result = eval("$#{stream}").string
15
+ ensure
16
+ eval("$#{stream} = #{stream.upcase}")
17
+ end
18
+
19
+ result
20
+ end
21
+
22
+ require File.expand_path("../../lib/ey_pro_cli", __FILE__)
23
+
24
+ Dir[File.expand_path("../{shared,support}/*.rb", __FILE__)].each{|f| require(f)}
25
+
26
+ if ENV["MOCK_CLI"]
27
+ Ey::Core::Client.mock!
28
+ Ey::Core::Client.reset!
29
+ end
30
+
31
+ RSpec.configure do |config|
32
+ config.order = "random"
33
+
34
+ config.before(:each) do
35
+ @tempfile = Tempfile.new(".ey-core")
36
+ end
37
+ end
@@ -0,0 +1,32 @@
1
+ module CoreHelper
2
+ def boot_environment(core_client)
3
+ account = core_client.accounts.create(name: SecureRandom.hex(4))
4
+ environment = account.environments.create(name: SecureRandom.hex(4))
5
+ application = account.applications.create(name: SecureRandom.hex(4))
6
+ provider = create_provider(account)
7
+ cluster = environment.clusters.create(name: SecureRandom.hex(4), location: "us-west-2", provider: provider)
8
+ app_component = core_client.components.first(name: "default_deployer")
9
+ www_cluster_component = cluster.cluster_components.create!(component: app_component, configuration: { application: application.id})
10
+ cluster.slots.create(quantity: 1)
11
+
12
+ [account, environment, application]
13
+ end
14
+
15
+ def create_provider(account)
16
+ attributes = {type: :aws, provisioned_id: SecureRandom.hex(8)}
17
+ attributes[:credentials] ||= {
18
+ :instance_aws_secret_id => SecureRandom.hex(6),
19
+ :instance_aws_secret_key => SecureRandom.hex(6),
20
+ :aws_secret_id => SecureRandom.hex(6),
21
+ :aws_secret_key => SecureRandom.hex(6),
22
+ :aws_login => FFaker::Internet.email,
23
+ :aws_pass => SecureRandom.hex(6),
24
+ }
25
+
26
+ account.providers.create!(attributes).resource!
27
+ end
28
+ end
29
+
30
+ RSpec.configure do |config|
31
+ config.include(CoreHelper)
32
+ end
@@ -0,0 +1,10 @@
1
+ module YamlHelper
2
+ def read_yaml(tempfile)
3
+ tempfile.rewind
4
+ YAML.load_file(tempfile)
5
+ end
6
+ end
7
+
8
+ RSpec.configure do |config|
9
+ config.include(YamlHelper)
10
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ey-pro-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Engine Yard Cloud Team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-07-24 00:00:00.000000000 Z
11
+ date: 2015-07-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -94,6 +94,48 @@ dependencies:
94
94
  - - ">="
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rspec
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '3.0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '3.0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: ffaker
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rake
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
97
139
  - !ruby/object:Gem::Dependency
98
140
  name: addressable
99
141
  requirement: !ruby/object:Gem::Requirement
@@ -207,9 +249,11 @@ extensions: []
207
249
  extra_rdoc_files: []
208
250
  files:
209
251
  - ".gitignore"
252
+ - ".travis.yml"
210
253
  - Gemfile
211
254
  - Gemfile.lock
212
255
  - README.md
256
+ - Rakefile
213
257
  - bin/ey-pro
214
258
  - ey-pro-cli.gemspec
215
259
  - lib/ey_pro_cli.rb
@@ -268,6 +312,7 @@ files:
268
312
  - lib/vendor/core/ey-core/collections/server_events.rb
269
313
  - lib/vendor/core/ey-core/collections/server_usages.rb
270
314
  - lib/vendor/core/ey-core/collections/servers.rb
315
+ - lib/vendor/core/ey-core/collections/services.rb
271
316
  - lib/vendor/core/ey-core/collections/slot_components.rb
272
317
  - lib/vendor/core/ey-core/collections/slots.rb
273
318
  - lib/vendor/core/ey-core/collections/ssl_certificates.rb
@@ -338,6 +383,7 @@ files:
338
383
  - lib/vendor/core/ey-core/models/server.rb
339
384
  - lib/vendor/core/ey-core/models/server_event.rb
340
385
  - lib/vendor/core/ey-core/models/server_usage.rb
386
+ - lib/vendor/core/ey-core/models/service.rb
341
387
  - lib/vendor/core/ey-core/models/slot.rb
342
388
  - lib/vendor/core/ey-core/models/slot_component.rb
343
389
  - lib/vendor/core/ey-core/models/ssl_certificate.rb
@@ -367,6 +413,7 @@ files:
367
413
  - lib/vendor/core/ey-core/requests/create_connector.rb
368
414
  - lib/vendor/core/ey-core/requests/create_database_server.rb
369
415
  - lib/vendor/core/ey-core/requests/create_database_service.rb
416
+ - lib/vendor/core/ey-core/requests/create_database_service_snapshot.rb
370
417
  - lib/vendor/core/ey-core/requests/create_environment.rb
371
418
  - lib/vendor/core/ey-core/requests/create_firewall.rb
372
419
  - lib/vendor/core/ey-core/requests/create_firewall_rule.rb
@@ -423,6 +470,7 @@ files:
423
470
  - lib/vendor/core/ey-core/requests/get_alert.rb
424
471
  - lib/vendor/core/ey-core/requests/get_alerting_environments.rb
425
472
  - lib/vendor/core/ey-core/requests/get_alerts.rb
473
+ - lib/vendor/core/ey-core/requests/get_api_token.rb
426
474
  - lib/vendor/core/ey-core/requests/get_application.rb
427
475
  - lib/vendor/core/ey-core/requests/get_application_archive.rb
428
476
  - lib/vendor/core/ey-core/requests/get_application_archives.rb
@@ -550,6 +598,12 @@ files:
550
598
  - lib/vendor/core/ey-core/subscribable.rb
551
599
  - lib/vendor/core/ey-core/token_authentication.rb
552
600
  - lib/vendor/core/ey-core/version.rb
601
+ - spec/deploy_spec.rb
602
+ - spec/login_spec.rb
603
+ - spec/logout_spec.rb
604
+ - spec/spec_helper.rb
605
+ - spec/support/core_helper.rb
606
+ - spec/support/yaml_helper.rb
553
607
  homepage:
554
608
  licenses: []
555
609
  metadata: {}
@@ -573,5 +627,11 @@ rubygems_version: 2.2.2
573
627
  signing_key:
574
628
  specification_version: 4
575
629
  summary: Command-line deployment for Engine Yard pro
576
- test_files: []
630
+ test_files:
631
+ - spec/deploy_spec.rb
632
+ - spec/login_spec.rb
633
+ - spec/logout_spec.rb
634
+ - spec/spec_helper.rb
635
+ - spec/support/core_helper.rb
636
+ - spec/support/yaml_helper.rb
577
637
  has_rdoc: