fastly 0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +6 -0
- data/Gemfile +4 -0
- data/README.md +59 -0
- data/Rakefile +1 -0
- data/bin/fastly_upload_vcl +67 -0
- data/fastly.gemspec +23 -0
- data/lib/fastly.rb +357 -0
- data/lib/fastly/backend.rb +99 -0
- data/lib/fastly/base.rb +62 -0
- data/lib/fastly/belongs_to_service_and_version.rb +42 -0
- data/lib/fastly/client.rb +160 -0
- data/lib/fastly/customer.rb +26 -0
- data/lib/fastly/director.rb +47 -0
- data/lib/fastly/domain.rb +29 -0
- data/lib/fastly/fetcher.rb +56 -0
- data/lib/fastly/invoice.rb +95 -0
- data/lib/fastly/match.rb +77 -0
- data/lib/fastly/origin.rb +23 -0
- data/lib/fastly/service.rb +112 -0
- data/lib/fastly/settings.rb +69 -0
- data/lib/fastly/user.rb +62 -0
- data/lib/fastly/vcl.rb +28 -0
- data/lib/fastly/version.rb +148 -0
- data/test/admin_test.rb +41 -0
- data/test/api_key_test.rb +52 -0
- data/test/common.rb +155 -0
- data/test/full_login_test.rb +98 -0
- data/test/helper.rb +24 -0
- metadata +157 -0
@@ -0,0 +1,56 @@
|
|
1
|
+
class Fastly
|
2
|
+
# :nodoc:
|
3
|
+
class AuthRequired < RuntimeError; end
|
4
|
+
# :nodoc:
|
5
|
+
class FullAuthRequired < Fastly::AuthRequired; end
|
6
|
+
# :nodoc:
|
7
|
+
class Error < RuntimeError; end
|
8
|
+
# :nodoc:
|
9
|
+
class Unauthorized < AuthRequired; end
|
10
|
+
|
11
|
+
module Fetcher # :nodoc: all
|
12
|
+
|
13
|
+
# Get the current Fastly::Client
|
14
|
+
def client(opts={})
|
15
|
+
opts[:base_url] ||= 'api.fastly.com'
|
16
|
+
opts[:base_port] ||= 80
|
17
|
+
@client ||= Fastly::Client.new(opts)
|
18
|
+
end
|
19
|
+
|
20
|
+
def list(klass, opts={})
|
21
|
+
raise Fastly::FullAuthRequired unless self.fully_authed?
|
22
|
+
list = client.get(klass.list_path, opts)
|
23
|
+
return [] if list.nil?
|
24
|
+
list.map { |hash| klass.new(hash, self) }
|
25
|
+
end
|
26
|
+
|
27
|
+
def get(klass, *args)
|
28
|
+
raise Fastly::FullAuthRequired unless self.fully_authed?
|
29
|
+
if [User, Customer].include?(klass) && args.empty?
|
30
|
+
hash = client.get("/current_#{klass.path}")
|
31
|
+
else
|
32
|
+
hash = client.get(klass.get_path(*args))
|
33
|
+
end
|
34
|
+
return nil if hash.nil?
|
35
|
+
return klass.new(hash, self)
|
36
|
+
end
|
37
|
+
|
38
|
+
def create(klass, opts)
|
39
|
+
raise Fastly::FullAuthRequired unless self.fully_authed?
|
40
|
+
hash = client.post(klass.post_path(opts),opts)
|
41
|
+
return klass.new(hash, self)
|
42
|
+
end
|
43
|
+
|
44
|
+
def update(klass, obj)
|
45
|
+
raise Fastly::FullAuthRequired unless self.fully_authed?
|
46
|
+
hash = client.put(klass.put_path(obj), obj.as_hash)
|
47
|
+
return klass.new(hash, self)
|
48
|
+
end
|
49
|
+
|
50
|
+
def delete(klass, obj)
|
51
|
+
raise Fastly::FullAuthRequired unless self.fully_authed?
|
52
|
+
return client.delete(klass.delete_path(obj))
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
class Fastly
|
2
|
+
# An invoice for a time period
|
3
|
+
class Invoice < Base
|
4
|
+
attr_accessor :service_id, :service_name, :start_time, :end_time, :total, :regions
|
5
|
+
|
6
|
+
##
|
7
|
+
# :attr: service_id
|
8
|
+
#
|
9
|
+
# The id of the service this invoice is for
|
10
|
+
#
|
11
|
+
|
12
|
+
##
|
13
|
+
# :attr: service_name
|
14
|
+
#
|
15
|
+
# The id of the service this invoice is for
|
16
|
+
#
|
17
|
+
|
18
|
+
##
|
19
|
+
# :attr: start_time
|
20
|
+
#
|
21
|
+
# The earliest date and time this invoice covers
|
22
|
+
#
|
23
|
+
|
24
|
+
##
|
25
|
+
# :attr: end_time
|
26
|
+
#
|
27
|
+
# The latest date and time this invoice covers
|
28
|
+
#
|
29
|
+
|
30
|
+
##
|
31
|
+
# :attr: total
|
32
|
+
#
|
33
|
+
# The total for this invoice in US dollars
|
34
|
+
#
|
35
|
+
|
36
|
+
##
|
37
|
+
# :attr: regions
|
38
|
+
#
|
39
|
+
# A hash reference with all the different regions and their subtotals
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def self.get_path(*args)
|
44
|
+
opts = args.size>0 ? args[0] : {}
|
45
|
+
url = "/billing"
|
46
|
+
if opts.has_key?(:service_id)
|
47
|
+
url += "/service/#{opts[:service_id]}"
|
48
|
+
end
|
49
|
+
if opts.has_key?(:year) && opts.has_key?(:month)
|
50
|
+
url += "/year/#{opts[:year]}/month/#{opts[:month]}"
|
51
|
+
end
|
52
|
+
url
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.list_path(*args)
|
56
|
+
get_path(*args)
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.post_path
|
60
|
+
raise "You can't POST to an invoice"
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.put_path
|
64
|
+
raise "You can't PUT to an invoice"
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.delete_path
|
68
|
+
raise "You can't DELETE to an invoice"
|
69
|
+
end
|
70
|
+
|
71
|
+
def save!
|
72
|
+
raise "You can't save an invoice"
|
73
|
+
end
|
74
|
+
|
75
|
+
def delete!
|
76
|
+
raise "You can't delete an invoice"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
|
81
|
+
# Return an array of Invoice objects representing invoices for all services.
|
82
|
+
#
|
83
|
+
# If a year and month are passed in returns the invoices for that whole month.
|
84
|
+
#
|
85
|
+
# Otherwise it returns the invoices for the current month so far.
|
86
|
+
def list_invoices(year=nil, month=nil)
|
87
|
+
opts = {}
|
88
|
+
unless year.nil? || month.nil?
|
89
|
+
opts[:year] = year
|
90
|
+
opts[:month] = month
|
91
|
+
end
|
92
|
+
list(Fastly::Invoice, opts)
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
data/lib/fastly/match.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
class Fastly
|
2
|
+
# An object that allows matching on requests and dispatching to different methods
|
3
|
+
class Match < BelongsToServiceAndVersion
|
4
|
+
attr_accessor :service_id, :name, :comment, :pattern, :priority, :on_recv, :on_lookup, :on_fetch, :on_deliver, :on_miss, :on_hit
|
5
|
+
|
6
|
+
##
|
7
|
+
# :attr: service_id
|
8
|
+
#
|
9
|
+
# The id of the service this belongs to.
|
10
|
+
#
|
11
|
+
|
12
|
+
##
|
13
|
+
# :attr: version
|
14
|
+
#
|
15
|
+
# The number of the version this belongs to.
|
16
|
+
#
|
17
|
+
|
18
|
+
##
|
19
|
+
# :attr: name
|
20
|
+
#
|
21
|
+
# The name of this match.
|
22
|
+
#
|
23
|
+
|
24
|
+
##
|
25
|
+
# :attr: pattern
|
26
|
+
#
|
27
|
+
# The matching pattern.
|
28
|
+
#
|
29
|
+
|
30
|
+
##
|
31
|
+
# :attr: on_recv
|
32
|
+
#
|
33
|
+
# What VCL action to execute before we lookup the object.
|
34
|
+
#
|
35
|
+
|
36
|
+
##
|
37
|
+
# :attr: on_lookup
|
38
|
+
#
|
39
|
+
# What VCL action to execute during a lookup.
|
40
|
+
#
|
41
|
+
|
42
|
+
##
|
43
|
+
# :attr: on_fetch
|
44
|
+
#
|
45
|
+
# What to execute after we have the header.
|
46
|
+
#
|
47
|
+
|
48
|
+
##
|
49
|
+
# :attr: on_miss
|
50
|
+
#
|
51
|
+
# What to execute on a cache miss
|
52
|
+
#
|
53
|
+
|
54
|
+
##
|
55
|
+
# :attr: on_hit
|
56
|
+
#
|
57
|
+
# What to execute on a cache hit.
|
58
|
+
#
|
59
|
+
|
60
|
+
##
|
61
|
+
# :attr: on_deliver
|
62
|
+
#
|
63
|
+
# What to execute just before delivering the object.
|
64
|
+
#
|
65
|
+
|
66
|
+
##
|
67
|
+
# :attr: priority
|
68
|
+
#
|
69
|
+
# The ordering of the match object
|
70
|
+
#
|
71
|
+
|
72
|
+
##
|
73
|
+
# :attr: comment
|
74
|
+
#
|
75
|
+
# a free form comment field
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class Fastly
|
2
|
+
# A logical group of directors - for example the asset server directors from all your DCNs
|
3
|
+
class Origin < BelongsToServiceAndVersion
|
4
|
+
attr_accessor :service_id, :name, :comment
|
5
|
+
|
6
|
+
##
|
7
|
+
# :attr: service_id
|
8
|
+
#
|
9
|
+
# The id of the service this belongs to.
|
10
|
+
#
|
11
|
+
|
12
|
+
##
|
13
|
+
# :attr: version
|
14
|
+
#
|
15
|
+
# The number of the version this belongs to.
|
16
|
+
#
|
17
|
+
|
18
|
+
##
|
19
|
+
# :attr: name
|
20
|
+
#
|
21
|
+
# The domain name of this domain
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
class Fastly
|
2
|
+
# Represents something you want to serve - this can be, for example, a whole web site, a Wordpress site, or just your image servers
|
3
|
+
class Service < Base
|
4
|
+
attr_accessor :id, :customer, :name, :comment
|
5
|
+
@versions = []
|
6
|
+
|
7
|
+
##
|
8
|
+
# :attr: id
|
9
|
+
#
|
10
|
+
# The id of the service
|
11
|
+
#
|
12
|
+
|
13
|
+
##
|
14
|
+
# :attr: customer
|
15
|
+
#
|
16
|
+
# The id of the customer this belongs to
|
17
|
+
#
|
18
|
+
|
19
|
+
##
|
20
|
+
# :attr: name
|
21
|
+
#
|
22
|
+
# The name of this service
|
23
|
+
#
|
24
|
+
|
25
|
+
##
|
26
|
+
# :attr: comment
|
27
|
+
#
|
28
|
+
# a free form comment field
|
29
|
+
|
30
|
+
|
31
|
+
##
|
32
|
+
#
|
33
|
+
# Get a hash of stats from different data centers.
|
34
|
+
#
|
35
|
+
# Type can be one of
|
36
|
+
# * minutely
|
37
|
+
# * hourly
|
38
|
+
# * daily
|
39
|
+
# * all
|
40
|
+
def stats(type=:all)
|
41
|
+
raise Fastly::FullAuthRequired unless fetcher.fully_authed?
|
42
|
+
raise Fastly::Error "Unknown stats type #{type}" unless [:minutely,:hourly,:daily,:all].include?(type.to_sym)
|
43
|
+
hash = fetcher.client.get(Fastly::Service.get_path(self.id)+"/stats/#{type}")
|
44
|
+
return hash
|
45
|
+
end
|
46
|
+
|
47
|
+
# Return a Invoice object representing the invoice for this service
|
48
|
+
#
|
49
|
+
# If a year and month are passed in returns the invoice for that whole month.
|
50
|
+
#
|
51
|
+
# Otherwise it returns the invoice for the current month so far.
|
52
|
+
def invoice(year=nil, month=nil)
|
53
|
+
raise Fastly::FullAuthRequired unless fetcher.fully_authed?
|
54
|
+
opts = { :service_id => self.id }
|
55
|
+
unless year.nil? || month.nil?
|
56
|
+
opts[:year] = year
|
57
|
+
opts[:month] = month
|
58
|
+
end
|
59
|
+
fetcher.get(Fastly::Invoice, opts)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Purge all assets from this service.
|
63
|
+
def purge_all
|
64
|
+
raise Fastly::AuthRequired unless self.authed?
|
65
|
+
res = client.put(get_path(self.id)+"/purge_all")
|
66
|
+
end
|
67
|
+
|
68
|
+
# Set all the versions that this service has had.
|
69
|
+
def versions=(versions)
|
70
|
+
@versions = versions
|
71
|
+
end
|
72
|
+
|
73
|
+
# Get a sorted array of all the versions that this service has had.
|
74
|
+
def versions
|
75
|
+
raise Fastly::FullAuthRequired unless fetcher.fully_authed?
|
76
|
+
versions = []
|
77
|
+
@versions.each_pair { |number, version|
|
78
|
+
versions.push Fastly::Version.new({ :number => number, :service_id => self.id, :comment => version['comment'] || "" }, fetcher)
|
79
|
+
}
|
80
|
+
versions.sort {|a,b| a.number.to_i <=> b.number.to_i }
|
81
|
+
end
|
82
|
+
|
83
|
+
# Get an individual Version object. By default returns the latest version
|
84
|
+
def version(number=-1)
|
85
|
+
raise Fastly::FullAuthRequired unless fetcher.fully_authed?
|
86
|
+
versions[number]
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
# Get a list of all the services that the current customer has.
|
92
|
+
def list_services(opts={})
|
93
|
+
list(Fastly::Service, opts)
|
94
|
+
end
|
95
|
+
|
96
|
+
# Search all the services that the current customer has.
|
97
|
+
#
|
98
|
+
# In general you'll want to do
|
99
|
+
#
|
100
|
+
# services = fastly.search_services(:name => name)
|
101
|
+
#
|
102
|
+
# or
|
103
|
+
#
|
104
|
+
# service = fastly.search_services(:name => name, :version => number)
|
105
|
+
def search_services(opts)
|
106
|
+
raise Fastly::FullAuthRequired unless self.fully_authed?
|
107
|
+
klass = Fastly::Service
|
108
|
+
hash = client.get(klass.post_path+"/search", opts)
|
109
|
+
return nil if hash.nil?
|
110
|
+
klass.new(hash, self)
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
class Fastly
|
2
|
+
# Represent arbitary key value settings for a given Version
|
3
|
+
class Settings < Base
|
4
|
+
attr_accessor :service_id, :version, :settings
|
5
|
+
##
|
6
|
+
# :attr: service_id
|
7
|
+
#
|
8
|
+
# The id of the service this belongs to.
|
9
|
+
#
|
10
|
+
|
11
|
+
##
|
12
|
+
# :attr: version
|
13
|
+
#
|
14
|
+
# The number of the version this belongs to.
|
15
|
+
#
|
16
|
+
|
17
|
+
##
|
18
|
+
# :attr: service
|
19
|
+
#
|
20
|
+
# Return a hash containing key/value pairs of settings
|
21
|
+
#
|
22
|
+
|
23
|
+
|
24
|
+
# :nodoc:
|
25
|
+
def self.get_path(service, number)
|
26
|
+
"/service/#{service}/version/#{number}/settings"
|
27
|
+
end
|
28
|
+
|
29
|
+
# :nodoc:
|
30
|
+
def self.put_path(obj)
|
31
|
+
get_path(obj.service_id, obj.version)
|
32
|
+
end
|
33
|
+
|
34
|
+
# :nodoc:
|
35
|
+
def self.list_path(opts={})
|
36
|
+
raise "You can't list settings"
|
37
|
+
end
|
38
|
+
|
39
|
+
# :nodoc:
|
40
|
+
def self.post_path
|
41
|
+
raise "You can't POST to an invoice"
|
42
|
+
end
|
43
|
+
|
44
|
+
# :nodoc:
|
45
|
+
def self.delete_path
|
46
|
+
raise "You can't DELETE to an invoice"
|
47
|
+
end
|
48
|
+
|
49
|
+
# :nodoc:
|
50
|
+
def delete!
|
51
|
+
raise "You can't delete an invoice"
|
52
|
+
end
|
53
|
+
|
54
|
+
# :nodoc:
|
55
|
+
def as_hash
|
56
|
+
settings
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Get the Settings object for the specified Version
|
61
|
+
def get_settings(service, number)
|
62
|
+
get(Fastly::Settings, service, number)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Update the Settings object for the specified Version
|
66
|
+
def update_settings(opts={})
|
67
|
+
update(Fastly::Settings, opts)
|
68
|
+
end
|
69
|
+
end
|
data/lib/fastly/user.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
class Fastly
|
2
|
+
# A representation of a User in Fastly
|
3
|
+
class User < Base
|
4
|
+
attr_accessor :id, :name, :login, :customer_id, :role, :password
|
5
|
+
##
|
6
|
+
# :attr: id
|
7
|
+
#
|
8
|
+
# The id of this user
|
9
|
+
#
|
10
|
+
|
11
|
+
##
|
12
|
+
# :attr: name
|
13
|
+
#
|
14
|
+
# The name of this user
|
15
|
+
#
|
16
|
+
|
17
|
+
##
|
18
|
+
# :attr: customer_id
|
19
|
+
#
|
20
|
+
# The id of the customer this user belongs to
|
21
|
+
#
|
22
|
+
|
23
|
+
##
|
24
|
+
# :attr: role
|
25
|
+
#
|
26
|
+
# The role this user has (one of admin, owner, superuser, user, engineer, billing)
|
27
|
+
|
28
|
+
|
29
|
+
# Get the Customer object this user belongs to
|
30
|
+
def customer
|
31
|
+
@customer ||= fetcher.get(Customer, self.customer_id)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Whether or not this User is the owner of the Customer they belong to
|
35
|
+
def owner?
|
36
|
+
customer.owner_id == id
|
37
|
+
end
|
38
|
+
|
39
|
+
# :nodoc:
|
40
|
+
PRIORITIES = {
|
41
|
+
:admin => 1,
|
42
|
+
:owner => 10,
|
43
|
+
:superuser => 10,
|
44
|
+
:user => 20,
|
45
|
+
:engineer => 30,
|
46
|
+
:billing => 30,
|
47
|
+
}
|
48
|
+
|
49
|
+
# Does this User have sufficient permissions to perform the given role
|
50
|
+
def can_do?(test_role)
|
51
|
+
test_priority = PRIORITIES[test_role.to_sym] || 1000
|
52
|
+
my_priority = PRIORITIES[self.role.to_sym] || 1000
|
53
|
+
|
54
|
+
if test_priority == my_priority
|
55
|
+
test_role.to_s == :owner ? owner? : test_role.to_sym == self.role.to_sym
|
56
|
+
else
|
57
|
+
my_priority < test_priority
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|