fastlyctl 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,88 @@
1
+ module FastlyCTL
2
+ class CLI < Thor
3
+ desc "acl ACTION ACL_NAME IP_OR_SUBNET", "Manipulate ACLS.\n Actions:\n create: Create an ACL\n
4
+ delete: Delete an ACL\n
5
+ list: Provide a list of ACLs on this service\n
6
+ add: Add an IP/subnet to an ACL\n
7
+ remove: Remove an IP/subnet from an ACL\n
8
+ list_ips: List all IPs/subnets in the ACL\n
9
+ bulk_add: Perform operations on the ACL in bulk. A list of operations in JSON format should be specified in the ip field. Documentation on this format can be found here: https://docs.fastly.com/api/config#acl_entry_c352ca5aee49b7898535cce488e3ba82"
10
+ method_option :service, :aliases => ["--s"]
11
+ method_option :version, :aliases => ["--v"]
12
+ method_option :negate, :aliases => ["--n"]
13
+ def acl(action, name=false, ip=false)
14
+ id = FastlyCTL::Utils.parse_directory unless options[:service]
15
+ id ||= options[:service]
16
+
17
+ abort "Could not parse service id from directory. Specify service id with --service or use from within service directory." unless id
18
+
19
+ version = FastlyCTL::Fetcher.get_writable_version(id) unless options[:version]
20
+ version ||= options[:version]
21
+
22
+ case action
23
+ when "create"
24
+ abort "Must specify name for ACL" unless name
25
+ FastlyCTL::Fetcher.api_request(:post, "/service/#{id}/version/#{version}/acl", params: { name: name })
26
+
27
+ say("ACL #{name} created.")
28
+ when "delete"
29
+ abort "Must specify name for ACL" unless name
30
+ FastlyCTL::Fetcher.api_request(:delete, "/service/#{id}/version/#{version}/acl/#{name}")
31
+
32
+ say("ACL #{name} deleted.")
33
+ when "list"
34
+ resp = FastlyCTL::Fetcher.api_request(:get, "/service/#{id}/version/#{version}/acl")
35
+
36
+ say("No ACLs on service in this version.") unless resp.length > 0
37
+
38
+ resp.each do |d|
39
+ puts "#{d["id"]} - #{d["name"]}"
40
+ end
41
+ when "add"
42
+ abort "Must specify name for ACL" unless name
43
+ abort "Must specify IP" unless ip
44
+ acl = FastlyCTL::Fetcher.api_request(:get, "/service/#{id}/version/#{version}/acl/#{name}")
45
+ FastlyCTL::Fetcher.api_request(:post, "/service/#{id}/acl/#{acl["id"]}/entry", params: { ip: ip, negated: options.key?(:negate) ? "1" : "0" })
46
+
47
+ say("#{ip} added to ACL #{name}.")
48
+ when "remove"
49
+ abort "Must specify name for ACL" unless name
50
+ abort "Must specify IP for ACL entry" unless ip
51
+ acl = FastlyCTL::Fetcher.api_request(:get, "/service/#{id}/version/#{version}/acl/#{name}")
52
+ entries = FastlyCTL::Fetcher.api_request(:get, "/service/#{id}/acl/#{acl["id"]}/entries")
53
+
54
+ entry = false
55
+ entries.each do |e|
56
+ if e["ip"] == ip
57
+ entry = e
58
+ break
59
+ end
60
+ end
61
+
62
+ abort "IP #{ip} not found in ACL" unless entry
63
+
64
+ FastlyCTL::Fetcher.api_request(:delete, "/service/#{id}/acl/#{acl["id"]}/entry/#{entry["id"]}")
65
+
66
+ say("IP #{ip} removed from ACL #{name}.")
67
+ when "list_ips"
68
+ abort "Must specify name for ACL" unless name
69
+ acl = FastlyCTL::Fetcher.api_request(:get, "/service/#{id}/version/#{version}/acl/#{name}")
70
+ entries = FastlyCTL::Fetcher.api_request(:get, "/service/#{id}/acl/#{acl["id"]}/entries")
71
+
72
+ say("No items in ACL.") unless entries.length > 0
73
+ entries.each do |i|
74
+ puts "#{i["ip"]} - Negated: #{i["negated"] == "0" ? "false" : "true"}"
75
+ end
76
+ when "bulk_add"
77
+ abort "Must specify name for ACL" unless name
78
+ abort "Must specify JSON blob of operations in ip field. Documentation on this can be found here: https://docs.fastly.com/api/config#acl_entry_c352ca5aee49b7898535cce488e3ba82" unless ip
79
+ acl = FastlyCTL::Fetcher.api_request(:get, "/service/#{id}/version/#{version}/acl/#{name}")
80
+ FastlyCTL::Fetcher.api_request(:patch, "/service/#{id}/acl/#{acl["id"]}/items", {body: ip, headers: {"Content-Type" => "application/json"}})
81
+
82
+ say("Bulk add operation completed successfully.")
83
+ else
84
+ abort "#{action} is not a valid command"
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,27 @@
1
+ module FastlyCTL
2
+ class CLI < Thor
3
+ desc "activate", "Activates the latest writable service version, or the version number provided in the --version flag."
4
+ method_option :service, :aliases => ["--s"]
5
+ method_option :version, :aliases => ["--v"]
6
+ method_option :comment, :aliases => ["--c"]
7
+ def activate
8
+ id = FastlyCTL::Utils.parse_directory unless options[:service]
9
+ id ||= options[:service]
10
+
11
+ abort "Could not parse service id from directory. Use --s <service> to specify, vcl download, then try again." unless id
12
+
13
+ writable_version = FastlyCTL::Fetcher.get_writable_version(id) unless options[:version]
14
+ writable_version ||= options[:version].to_i
15
+
16
+ if options.key?(:comment)
17
+ FastlyCTL::Fetcher.api_request(:put, "/service/#{id}/version/#{writable_version}",{
18
+ params: {comment: options[:comment]}
19
+ })
20
+ end
21
+
22
+ FastlyCTL::Fetcher.api_request(:put, "/service/#{id}/version/#{writable_version}/activate")
23
+
24
+ say("Version #{writable_version} on #{id} activated.")
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,40 @@
1
+ module FastlyCTL
2
+ class CLI < Thor
3
+ desc "clone SERVICE_ID TARGET_SERVICE_ID", "Clone a service version to another service."
4
+ method_option :version, :aliases => ["--v"]
5
+ method_option :skip_logging, :aliases => ["--sl"]
6
+ def clone(id,target_id)
7
+ version = FastlyCTL::Fetcher.get_active_version(id) unless options[:version]
8
+ version ||= options[:version]
9
+
10
+ target_version = FastlyCTL::Fetcher.api_request(:post, "/service/#{target_id}/version")["number"]
11
+
12
+ puts "Copying #{id} version #{version} to #{target_id} version #{target_version}..."
13
+
14
+ FastlyCTL::CloneUtils::OBJECT_TYPES.each do |type,meta|
15
+ next if (type.include?("logging/") && options.key?(:skip_logging))
16
+
17
+ response = FastlyCTL::Fetcher.api_request(:get, "/service/#{id}/version/#{version}/#{type}")
18
+ response = [response] unless response.is_a?(Array)
19
+
20
+ next unless response.length > 0
21
+
22
+ puts "Copying #{response.length} " + (response.length == 1 ? FastlyCTL::CloneUtils.unpluralize(type) : FastlyCTL::CloneUtils.pluralize(type))
23
+
24
+ response.each do |obj|
25
+ FastlyCTL::CloneUtils.copy(obj,type,target_id,target_version)
26
+ end
27
+ end
28
+
29
+ target_active_version = FastlyCTL::Fetcher.get_active_version(target_id)
30
+ response = FastlyCTL::Fetcher.api_request(:get, "/service/#{target_id}/version/#{target_active_version}/domain")
31
+ return unless response.length > 0
32
+
33
+ puts "Restoring #{response.length} " + (response.length == 1 ? "domain" : "domains" + " from #{target_id} version #{target_active_version}...")
34
+
35
+ response.each do |domain|
36
+ FastlyCTL::CloneUtils.copy(domain,"domain",target_id,target_version)
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,41 @@
1
+ module FastlyCTL
2
+ class CLI < Thor
3
+ desc "copy SERVICE_ID TARGET_SERVICE_ID OBJECT_TYPE OBJECT_NAME", "Copy an object from one service to another\n Available Object Types: #{FastlyCTL::CloneUtils::OBJECT_TYPES.keys.join(', ')}"
4
+ method_option :version1, :aliases => ["--v1"]
5
+ method_option :version2, :aliases => ["--v2"]
6
+ def copy(id,target_id,obj_type,obj_name=false)
7
+ abort "Object name must be specified for all object types except settings" if (obj_name === false && obj_type != "settings")
8
+
9
+ source_version = FastlyCTL::Fetcher.get_active_version(id) unless options[:version1]
10
+ source_version ||= options[:version1]
11
+ target_version = FastlyCTL::Fetcher.get_writable_version(target_id) unless options[:version2]
12
+ target_version ||= options[:version2]
13
+
14
+ unless FastlyCTL::CloneUtils::OBJECT_TYPES.include?(obj_type)
15
+ abort "Object type #{obj_type} is invalid. Must be one of: #{FastlyCTL::CloneUtils::OBJECT_TYPES.keys.join(', ')}"
16
+ end
17
+
18
+ path = "/service/#{id}/version/#{source_version}/#{obj_type}"
19
+ path += "/#{obj_name}" unless obj_type == "settings"
20
+ obj = FastlyCTL::Fetcher.api_request(:get, path)
21
+
22
+ if (obj_type == "settings")
23
+ puts "Copying settings from #{id} version #{source_version} to #{target_id} version #{target_version}..."
24
+ else
25
+ existing_obj = FastlyCTL::Fetcher.api_request(:get, "/service/#{target_id}/version/#{target_version}/#{obj_type}/#{obj_name}",{
26
+ expected_responses: [200,404]
27
+ })
28
+
29
+ if existing_obj.key?("name")
30
+ abort unless yes?("A #{FastlyCTL::CloneUtils.unpluralize(obj_type)} named #{obj_name} already exists on #{target_id} version #{target_version}. Delete it and proceed?")
31
+
32
+ FastlyCTL::Fetcher.api_request(:delete,"/service/#{target_id}/version/#{target_version}/#{obj_type}/#{obj_name}")
33
+ end
34
+
35
+ puts "Copying #{FastlyCTL::CloneUtils.unpluralize(obj_type)} #{obj_name} from #{id} version #{source_version} to #{target_id} version #{target_version}..."
36
+ end
37
+
38
+ FastlyCTL::CloneUtils.copy(obj,obj_type,target_id,target_version)
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,18 @@
1
+ module FastlyCTL
2
+ class CLI < Thor
3
+ desc "create_service SERVICE_NAME", "Create a blank service. If --customer is supplied and you are an admin, the command will move the service to that customer's account."
4
+ method_option :customer, :aliases => ["--c"]
5
+ def create_service(name)
6
+ service = FastlyCTL::Fetcher.api_request(:post, "/service", { params: { name: name }})
7
+
8
+ if options[:customer]
9
+ say("This command works by creating a service on your account and moving it to the target account.")
10
+ self.move(service["id"],options[:customer])
11
+ end
12
+
13
+ if yes?("Service #{service["id"]} has been created. Would you like to open the configuration page?")
14
+ FastlyCTL::Utils.open_service(service["id"])
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,75 @@
1
+ module FastlyCTL
2
+ class CLI < Thor
3
+ desc "dictionary ACTION DICTIONARY_NAME KEY VALUE", "Manipulate edge dictionaries.\n Actions:\n create: Create a dictionary\n
4
+ delete: Delete a dictionary\n
5
+ list: Provide a list of dictionaries on this service\n
6
+ upsert: Update a key in a dictionary if it exists. Add the key if it does not.\n
7
+ remove: Remove a key from a dictionary\n
8
+ list_items: List all keys in the dictionary\n
9
+ bulk_add: Perform operations on the dictionary in bulk. A list of operations in JSON format should be specified in the key field. Documentation on this format can be found here: https://docs.fastly.com/api/config#dictionary_item_dc826ce1255a7c42bc48eb204eed8f7f"
10
+ method_option :service, :aliases => ["--s"]
11
+ method_option :version, :aliases => ["--v"]
12
+ def dictionary(action, name=false, key=false, value=false)
13
+ id = FastlyCTL::Utils.parse_directory unless options[:service]
14
+ id ||= options[:service]
15
+
16
+ abort "Could not parse service id from directory. Specify service id with --service or use from within service directory." unless id
17
+
18
+ version = FastlyCTL::Fetcher.get_writable_version(id) unless options[:version]
19
+ version ||= options[:version]
20
+
21
+ case action
22
+ when "create"
23
+ abort "Must specify name for dictionary" unless name
24
+ FastlyCTL::Fetcher.api_request(:post, "/service/#{id}/version/#{version}/dictionary", params: { name: name })
25
+
26
+ say("Dictionary #{name} created.")
27
+ when "delete"
28
+ abort "Must specify name for dictionary" unless name
29
+ FastlyCTL::Fetcher.api_request(:delete, "/service/#{id}/version/#{version}/dictionary/#{name}")
30
+
31
+ say("Dictionary #{name} deleted.")
32
+ when "list"
33
+ resp = FastlyCTL::Fetcher.api_request(:get, "/service/#{id}/version/#{version}/dictionary")
34
+
35
+ say("No dictionaries on service in this version.") unless resp.length > 0
36
+
37
+ resp.each do |d|
38
+ puts "#{d["id"]} - #{d["name"]}"
39
+ end
40
+ when "upsert"
41
+ abort "Must specify name for dictionary" unless name
42
+ abort "Must specify key and value for dictionary item" unless (key && value)
43
+ dict = FastlyCTL::Fetcher.api_request(:get, "/service/#{id}/version/#{version}/dictionary/#{name}")
44
+ FastlyCTL::Fetcher.api_request(:put, "/service/#{id}/dictionary/#{dict["id"]}/item/#{key}", params: { item_value: value })
45
+
46
+ say("Dictionary item #{key} set to #{value}.")
47
+ when "remove"
48
+ abort "Must specify name for dictionary" unless name
49
+ abort "Must specify key for dictionary item" unless key
50
+ dict = FastlyCTL::Fetcher.api_request(:get, "/service/#{id}/version/#{version}/dictionary/#{name}")
51
+ FastlyCTL::Fetcher.api_request(:delete, "/service/#{id}/dictionary/#{dict["id"]}/item/#{key}")
52
+
53
+ say("Item #{key} removed from dictionary #{name}.")
54
+ when "list_items"
55
+ abort "Must specify name for dictionary" unless name
56
+ dict = FastlyCTL::Fetcher.api_request(:get, "/service/#{id}/version/#{version}/dictionary/#{name}")
57
+ resp = FastlyCTL::Fetcher.api_request(:get, "/service/#{id}/dictionary/#{dict["id"]}/items")
58
+
59
+ say("No items in dictionary.") unless resp.length > 0
60
+ resp.each do |i|
61
+ puts "#{i["item_key"]} : #{i["item_value"]}"
62
+ end
63
+ when "bulk_add"
64
+ abort "Must specify name for dictionary" unless name
65
+ abort "Must specify JSON blob of operations in key field. Documentation on this can be found here: https://docs.fastly.com/api/config#dictionary_item_dc826ce1255a7c42bc48eb204eed8f7f" unless key
66
+ dict = FastlyCTL::Fetcher.api_request(:get, "/service/#{id}/version/#{version}/dictionary/#{name}")
67
+ FastlyCTL::Fetcher.api_request(:patch, "/service/#{id}/dictionary/#{dict["id"]}/items", {body: key, headers: {"Content-Type" => "application/json"}})
68
+
69
+ say("Bulk add operation completed successfully.")
70
+ else
71
+ abort "#{action} is not a valid command"
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,97 @@
1
+ module FastlyCTL
2
+ class CLI < Thor
3
+ desc "diff", "Diff two service versions. By default, diffs the active version of the service assumed from the current directory with the local VCL in the current directory. Options allow you to specify different versions and different services."
4
+ method_option :version1, :aliases => ["--v1"]
5
+ method_option :version2, :aliases => ["--v2"]
6
+ method_option :service1, :aliases => ["--s1"]
7
+ method_option :service2, :aliases => ["--s2"]
8
+ method_option :generated, :aliases => ["--g"]
9
+ def diff
10
+ if options[:service1]
11
+ service1 = options[:service1]
12
+ else
13
+ service1 = FastlyCTL::Utils.parse_directory
14
+ abort "Could not parse service id from directory" unless service1
15
+ end
16
+ if options[:service2]
17
+ service2 = options[:service2]
18
+ else
19
+ service2 = FastlyCTL::Utils.parse_directory
20
+
21
+ # use service1 for both if unspecified
22
+ service2 = service1 unless service2
23
+ end
24
+
25
+ # diffing different services - no references to local vcl here
26
+ if service1 != service2
27
+ version1 = options.key?(:version1) ? options[:version1] : FastlyCTL::Fetcher.get_active_version(service1)
28
+ version2 = options.key?(:version2) ? options[:version2] : FastlyCTL::Fetcher.get_active_version(service2)
29
+ end
30
+
31
+ # diffing the same service
32
+ if service1 == service2
33
+ # if both are specified, diff them
34
+ if options[:version1] && options[:version2]
35
+ version1 = options[:version1]
36
+ version2 = options[:version2]
37
+ end
38
+ # if version1 is not specified, diff local with version 2
39
+ if !options[:version1] && options[:version2]
40
+ version1 = false
41
+ version2 = options[:version2]
42
+ end
43
+ # if version2 is not specified, diff local with version 1
44
+ if options[:version1] && !options[:version2]
45
+ version1 = options[:version1]
46
+ version2 = false
47
+ end
48
+ if !options[:version1] && !options[:version2]
49
+ # if neither are specified, diff local with active version
50
+ version1 = FastlyCTL::Fetcher.get_active_version(service2)
51
+ version2 = false
52
+ end
53
+ end
54
+
55
+ say("Diffing#{options[:generated] ? " generated VCL for" : ""} #{service1} #{version1 ? "version "+version1.to_s : "local VCL"} with #{service2} #{version2 ? "version "+version2.to_s : "local VCL"}.")
56
+
57
+ if version1
58
+ v1_vcls = FastlyCTL::Fetcher.get_vcl(service1, version1,options[:generated])
59
+ else
60
+ abort "Cannot diff generated VCL with local VCL" if options[:generated]
61
+ Dir.foreach(Dir.pwd) do |p|
62
+ next unless File.file?(p)
63
+ next unless p =~ /\.vcl$/
64
+
65
+ v1_vcls ||= Array.new
66
+ v1_vcls << {
67
+ "name" => p.chomp(".vcl"),
68
+ "content" => File.read(p)
69
+ }
70
+ end
71
+ end
72
+
73
+ if version2
74
+ v2_vcls = FastlyCTL::Fetcher.get_vcl(service2, version2,options[:generated])
75
+ else
76
+ abort "Cannot diff generated VCL with local VCL" if options[:generated]
77
+ Dir.foreach(Dir.pwd) do |p|
78
+ next unless File.file?(p)
79
+ next unless p =~ /\.vcl$/
80
+
81
+ v2_vcls ||= Array.new
82
+ v2_vcls << {
83
+ "name" => p.chomp(".vcl"),
84
+ "content" => File.read(p)
85
+ }
86
+ end
87
+ end
88
+
89
+ if options[:generated]
90
+ say(FastlyCTL::Utils.diff_generated(v1_vcls,v2_vcls))
91
+ else
92
+ say(FastlyCTL::Utils.diff_versions(v1_vcls,v2_vcls))
93
+ end
94
+
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,47 @@
1
+ module FastlyCTL
2
+ class CLI < Thor
3
+ desc "domain ACTION HOST", "Manipulate domains on a service. Available actions are create, delete, list and check. Create, delete and check take a host argument. Additionally, check can take the argument \"all\" to check all domains."
4
+ method_option :service, :aliases => ["--s"]
5
+ method_option :version, :aliases => ["--v"]
6
+ def domain(action,host=false)
7
+ id = FastlyCTL::Utils.parse_directory unless options[:service]
8
+ id ||= options[:service]
9
+
10
+ abort "Could not parse service id from directory. Use --s <service> to specify, vcl download, then try again." unless id
11
+
12
+ version = FastlyCTL::Fetcher.get_writable_version(id) unless options[:version]
13
+ version ||= options[:version].to_i
14
+
15
+ case action
16
+ when "create"
17
+ FastlyCTL::Fetcher.api_request(:post,"/service/#{id}/version/#{version}/domain",{
18
+ params: {
19
+ name: host,
20
+ }
21
+ })
22
+ say("#{host} created on #{id} version #{version}")
23
+ when "delete"
24
+ FastlyCTL::Fetcher.api_request(:delete,"/service/#{id}/version/#{version}/domain/#{host}")
25
+ say("#{host} deleted on #{id} version #{version}")
26
+ when "list"
27
+ domains = FastlyCTL::Fetcher.api_request(:get,"/service/#{id}/version/#{version}/domain")
28
+ say("Listing all domains for #{id} version #{version}")
29
+ domains.each do |d|
30
+ puts d["name"]
31
+ end
32
+ when "check"
33
+ if host == "all"
34
+ domains = FastlyCTL::Fetcher.api_request(:get,"/service/#{id}/version/#{version}/domain/check_all")
35
+ else
36
+ domains = [FastlyCTL::Fetcher.api_request(:get,"/service/#{id}/version/#{version}/domain/#{host}/check")]
37
+ end
38
+
39
+ domains.each do |d|
40
+ say("#{d[0]["name"]} -> #{d[1]}")
41
+ end
42
+ else
43
+ abort "#{action} is not a valid command"
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,85 @@
1
+ module FastlyCTL
2
+ class CLI < Thor
3
+ desc "download VCL_NAME", "Download VCLs. If no name is specified, downloads all the VCLs on the service."
4
+ method_option :service, :aliases => ["--s"]
5
+ method_option :version, :aliases => ["--v"]
6
+ method_option :generated, :aliases => ["--g"]
7
+ def download(vcl_name=false)
8
+ parsed_id = FastlyCTL::Utils.parse_directory
9
+
10
+ if options[:service]
11
+ abort "Already in a service directory, go up one level in order to specify"\
12
+ "service id with --service." if parsed_id
13
+ id = options[:service]
14
+ parsed = false
15
+ else
16
+ abort "Could not parse service id from directory. Specify service id with "\
17
+ "--service option or use from within service directory." unless parsed_id
18
+ id = parsed_id
19
+ parsed = true
20
+ end
21
+
22
+ service = FastlyCTL::Fetcher.api_request(:get, "/service/#{id}/details")
23
+
24
+ version = FastlyCTL::Fetcher.get_active_version(id) unless options[:version]
25
+ version ||= options[:version]
26
+
27
+ if options[:generated]
28
+ generated = FastlyCTL::Fetcher.api_request(:get, "/service/#{id}/version/#{version}/generated_vcl")
29
+ File.open("generated.vcl", 'w+') {|f| f.write(generated["content"]) }
30
+ abort "Generated VCL for version #{version} written to generated.vcl."
31
+ end
32
+
33
+ vcl = FastlyCTL::Fetcher.get_vcl(id, version)
34
+ snippet = FastlyCTL::Fetcher.get_snippets(id, version)
35
+
36
+ sname = service["name"]
37
+ if sname.include? "/"
38
+ sname = sname.tr("/","_")
39
+ end
40
+
41
+ folder_name = parsed ? "./" : "#{sname} - #{service["id"]}/"
42
+ Dir.mkdir(folder_name) unless (File.directory?(folder_name) || parsed)
43
+
44
+ if vcl
45
+ vcl.each do |v,k|
46
+ next if (vcl_name && vcl_name != v["name"])
47
+
48
+ filename = "#{folder_name}#{v["name"]}.vcl"
49
+
50
+ if File.exist?(filename)
51
+ unless yes?("Are you sure you want to overwrite #{filename}")
52
+ say("Skipping #{filename}")
53
+ next
54
+ end
55
+ end
56
+
57
+ File.open(filename, 'w+') {|f| f.write(v["content"]) }
58
+
59
+ say("VCL content for version #{version} written to #{filename}")
60
+ end
61
+ end
62
+
63
+ if snippet
64
+ snippet.each do |s,k|
65
+ filename = "#{folder_name}#{s["name"]}.snippet"
66
+
67
+ if File.exist?(filename)
68
+ unless yes?("Are you sure you want to overwrite #{filename}")
69
+ say("Skipping #{filename}")
70
+ next
71
+ end
72
+ end
73
+
74
+ File.open(filename, 'w+') {|f| f.write(s["content"]) }
75
+
76
+ say("Snippet content for version #{version} written to #{filename}")
77
+ end
78
+ end
79
+
80
+ unless vcl || snippet
81
+ say("No VCLs or snippets on this service, however a folder has been created. Create VCLs in this folder and upload.")
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,58 @@
1
+ module FastlyCTL
2
+ class CLI < Thor
3
+ desc "login", "Logs into the app. Required before doing anything else."
4
+ def login
5
+ if FastlyCTL::Token
6
+ abort unless yes?("You already have an access token, are you sure you want to authenticate again?")
7
+ end
8
+
9
+ if yes?("Does your organization use SSO to login to Fastly? If so, type \"yes\" and create a 'global' or 'root' scoped token in your web browser. Copy the token to the file ~/.fastlyctl_token and save it.")
10
+ Launchy.open(FastlyCTL::FASTLY_APP + "/account/personal/tokens/new")
11
+ abort
12
+ end
13
+
14
+ say("Proceeding with username/password login...")
15
+
16
+ login_results = FastlyCTL::Fetcher.login
17
+
18
+ File.open(FastlyCTL::COOKIE_JAR , 'w+') {|f| f.write(JSON.dump(FastlyCTL::Cookies)) }
19
+ File.chmod(0600, FastlyCTL::COOKIE_JAR)
20
+
21
+ say("Creating root scoped token...")
22
+
23
+ if login_results[:user].include?("@fastly.com") && !login_results[:user].include?("+")
24
+ scope = "root"
25
+ else
26
+ scope = "global"
27
+ end
28
+
29
+ o = {
30
+ user: login_results[:user],
31
+ pass: login_results[:pass],
32
+ code: login_results[:code],
33
+ scope: scope,
34
+ name: "fastlyctl_token"
35
+ }
36
+
37
+ resp = FastlyCTL::Fetcher.create_token(o)
38
+
39
+ token = resp["access_token"]
40
+ token_id = resp["id"]
41
+
42
+ File.open(FastlyCTL::TOKEN_FILE , 'w+') {|f| f.write(token) }
43
+ File.chmod(0600, FastlyCTL::TOKEN_FILE)
44
+
45
+ resp = FastlyCTL::Fetcher.api_request(:get, "/tokens", { headers: {"Fastly-Key" => token}})
46
+ abort unless resp.count > 0
47
+
48
+ resp.each do |t|
49
+ next unless (t["name"] == "fastlyctl_token" && t["id"] != token_id)
50
+
51
+ if yes?("There was already a token created with the name fastlyctl_token. To avoid creating multiple tokens, should it be deleted?")
52
+ FastlyCTL::Fetcher.api_request(:delete, "/tokens/#{t["id"]}", {headers: {"Fastly-Key" => token}, expected_responses: [204]})
53
+ say("Token with id #{t["id"]} deleted.")
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,24 @@
1
+ module FastlyCTL
2
+ class CLI < Thor
3
+ desc "open DOMAIN", "Find the service ID for a domain and open the Fastly app. You may also specify the service ID or assume the context of the directory you are in by omitting the domain."
4
+ method_option :service, :aliases => ["--s"]
5
+ def open(domain=false)
6
+ if (options[:service] && domain)
7
+ say("Both domain and service id supplied, using service id.")
8
+ domain = false
9
+ end
10
+
11
+ if options[:service]
12
+ id = options[:service]
13
+ elsif domain
14
+ id = FastlyCTL::Fetcher.domain_to_service_id(domain)
15
+ else
16
+ id = FastlyCTL::Utils.parse_directory
17
+
18
+ abort "Could not parse service id from directory. Use the --s <service> flag OR vcl open <domain>." unless id
19
+ end
20
+
21
+ FastlyCTL::Utils.open_service(id)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,17 @@
1
+ module FastlyCTL
2
+ class CLI < Thor
3
+ desc "purge_all", "Purge all content from a service."
4
+ method_option :service, :aliases => ["--s"]
5
+ def purge_all
6
+ parsed_id = FastlyCTL::Utils.parse_directory
7
+
8
+ id = FastlyCTL::Utils.parse_directory
9
+
10
+ abort "Could not parse service id from directory. Use --s <service> to specify, vcl download, then try again." unless (id || options[:service])
11
+
12
+ FastlyCTL::Fetcher.api_request(:post, "/service/#{id}/purge_all")
13
+
14
+ say("Purge all on #{id} completed.")
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,24 @@
1
+ module FastlyCTL
2
+ class CLI < Thor
3
+ desc "skeleton NAME", "Create a skeleton VCL file with the current boilerplate."
4
+ method_option :service, :aliases => ["--s"]
5
+ def skeleton(name="main")
6
+ id = FastlyCTL::Utils.parse_directory unless options[:service]
7
+ id ||= options[:service]
8
+ abort "Could not parse service id from directory. Use --s <service> to specify, vcl download, then try again." unless id
9
+
10
+ filename = "#{name}.vcl"
11
+ version = FastlyCTL::Fetcher.get_active_version(id)
12
+ boilerplate = FastlyCTL::Fetcher.api_request(:get, "/service/#{id}/version/#{version}/boilerplate")
13
+
14
+ if (File.exist?(filename))
15
+ say("#{filename} exists, please delete it if you want this command to overwrite it.")
16
+ abort
17
+ end
18
+
19
+ File.open(filename , 'w+') {|f| f.write(boilerplate) }
20
+
21
+ say("Boilerplate written to #{filename}.")
22
+ end
23
+ end
24
+ end