dockistrano 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,81 @@
1
+ require 'spec_helper'
2
+
3
+ describe Dockistrano::Hipache do
4
+
5
+ subject { described_class.new("127.0.0.1") }
6
+ let(:redis) { double.as_null_object }
7
+
8
+ before do
9
+ allow(subject).to receive(:redis).and_return(redis)
10
+ end
11
+
12
+ context "#online?" do
13
+ it "returns true when Hipache is online" do
14
+ allow(redis).to receive(:ping).and_return(true)
15
+ expect(subject.online?).to be_true
16
+ end
17
+
18
+ it "returns false when Hipache is offline" do
19
+ allow(redis).to receive(:ping).and_raise(Redis::CannotConnectError)
20
+ expect(subject.online?).to be_false
21
+ end
22
+ end
23
+
24
+ context "#wait_for_online" do
25
+ it "waits until Hipache is online" do
26
+ expect(subject).to receive(:online?).and_return(false, false, false, true)
27
+ expect(Kernel).to receive(:sleep).exactly(3).times
28
+ subject.wait_for_online
29
+ end
30
+
31
+ it "waits for a maximum of 5 seconds" do
32
+ expect(subject).to receive(:online?).and_return(false, false, false, false, false, false)
33
+ expect(Kernel).to receive(:sleep).exactly(5).times
34
+ subject.wait_for_online
35
+ end
36
+ end
37
+
38
+ context "#register" do
39
+ before do
40
+ allow(subject).to receive(:wait_for_online)
41
+ allow(subject).to receive(:online?).and_return(true)
42
+ end
43
+
44
+ it "waits for Hipache to be online" do
45
+ expect(subject).to receive(:wait_for_online)
46
+ subject.register("foobar", "application.dev", "33.33.33.33", "80")
47
+ end
48
+
49
+ it "removes any previously used addresses for the host" do
50
+ expect(redis).to receive(:lrange).with("frontend:application.dev", 0, -1).and_return(["33.33.33.33:80"])
51
+ expect(redis).to receive(:del).with("frontend:application.dev")
52
+ subject.register("foobar", "application.dev", "33.33.33.33", "80")
53
+ end
54
+
55
+ it "creates a new host in Hipache" do
56
+ expect(redis).to receive(:rpush).with("frontend:application.dev", "foobar")
57
+ expect(redis).to receive(:rpush).with("frontend:application.dev", "http://33.33.33.33:80")
58
+ subject.register("foobar", "application.dev", "33.33.33.33", "80")
59
+ end
60
+ end
61
+
62
+ context "#unregister" do
63
+ it "removes the ip address from Hipache" do
64
+ expect(subject).to receive(:online?).and_return(true)
65
+ expect(redis).to receive(:lrem).with("frontend:application.dev", 0, "http://33.33.33.33:80")
66
+ subject.unregister("foobar", "application.dev", "33.33.33.33", "80")
67
+ end
68
+ end
69
+
70
+ context "#status" do
71
+ it "returns a hash with registered hosts in Hipache" do
72
+ allow(subject).to receive(:online?).and_return(true)
73
+ expect(redis).to receive(:keys).with("frontend:*").and_return(["frontend:application.dev"])
74
+ expect(redis).to receive(:lrange).with("frontend:application.dev", 1, -1).and_return(["http://33.33.33.33:80"])
75
+ expect(subject.status).to eq({
76
+ "application.dev" => ["http://33.33.33.33:80"]
77
+ })
78
+ end
79
+ end
80
+
81
+ end
@@ -0,0 +1,56 @@
1
+ require 'spec_helper'
2
+
3
+ describe Dockistrano::Registry do
4
+
5
+ subject { described_class.new("registry.dev") }
6
+
7
+ context "#tags_for_image" do
8
+ it "returns all available tags on the registry" do
9
+ stub_request(:get, "http://registry.dev/v1/repositories/image_name/tags").to_return({
10
+ status: 200,
11
+ body: '{"develop": "49f387cc90f2d5b82ded91c239b6e583f8b955cb532912cc959b1d1289b3f8f1"}'
12
+ })
13
+
14
+ expect(subject.tags_for_image("image_name")).to eq({
15
+ "develop" => "49f387cc90f2d5b82ded91c239b6e583f8b955cb532912cc959b1d1289b3f8f1"
16
+ })
17
+ end
18
+
19
+ it "raises an error when the repository is not found in the registry" do
20
+ stub_request(:get, "http://registry.dev/v1/repositories/foobar/tags").to_return({
21
+ status: 404,
22
+ body: '{"error": "Repository not found"}'
23
+ })
24
+ expect { subject.tags_for_image("foobar") }.to raise_error(Dockistrano::Registry::RepositoryNotFoundInRegistry)
25
+ end
26
+
27
+ it "raises an error when the request failed" do
28
+ stub_request(:get, "http://registry.dev/v1/repositories/foobar/tags").to_return({
29
+ status: 500,
30
+ body: '{"error": "Something else"}'
31
+ })
32
+ expect { subject.tags_for_image("foobar") }.to raise_error("Something else")
33
+ end
34
+ end
35
+
36
+ context "#latest_id_for_image" do
37
+ it "returns the id for the image in the registry" do
38
+ stub_request(:get, "http://registry.dev/v1/repositories/foobar/tags/develop").to_return({
39
+ status: 200,
40
+ body: '"49f387cc90f2d5b82ded91c239b6e583f8b955cb532912cc959b1d1289b3f8f1"'
41
+ })
42
+
43
+ expect(subject.latest_id_for_image("foobar", "develop")).to eq("49f387cc90f2d5b82ded91c239b6e583f8b955cb532912cc959b1d1289b3f8f1")
44
+ end
45
+
46
+ it "returns nil when the image is not available in the registry" do
47
+ stub_request(:get, "http://registry.dev/v1/repositories/foobar/tags/develop").to_return({
48
+ status: 404,
49
+ body: '{"error": "Tag not found"}'
50
+ })
51
+
52
+ expect(subject.latest_id_for_image("foobar", "develop")).to be_nil
53
+ end
54
+ end
55
+
56
+ end
@@ -0,0 +1,154 @@
1
+ require 'spec_helper'
2
+
3
+ describe Dockistrano::ServiceDependency do
4
+
5
+ subject { described_class.new(service, "postgresql", { database: "application_development" }) }
6
+ let(:service) { double(tag: "develop", registry: "my.registry.net") }
7
+ let(:service_dependency) { double }
8
+
9
+ context ".factory" do
10
+ it "creates a new service based on the name" do
11
+ expect(described_class).to receive(:new).and_return(service_dependency)
12
+ expect(service_dependency).to receive(:backing_service).and_return(service)
13
+ expect(described_class.factory(service, "redis", { foo: "bar" })).to eq(service)
14
+ end
15
+ end
16
+
17
+ context "#initialize" do
18
+ before do
19
+ allow(subject).to receive(:load_config).and_return({ "default" => {} })
20
+ allow(subject).to receive(:tag_with_fallback).and_return("develop")
21
+ end
22
+
23
+ it "backing service has registry of the service" do
24
+ expect(subject.backing_service.registry).to eq("my.registry.net")
25
+ end
26
+
27
+ it "backing service has the name of the dependency" do
28
+ expect(subject.backing_service.image_name).to eq("postgresql")
29
+ end
30
+
31
+ it "backing service has the tag of the service" do
32
+ expect(subject.backing_service.tag).to eq("develop")
33
+ end
34
+
35
+ it "backing service has backing service environment variables from configuration" do
36
+ expect(subject.backing_service.backing_service_env).to eq({ database: "application_development" })
37
+ end
38
+
39
+ it "sets the tag with a fallback" do
40
+ expect(subject).to receive(:tag_with_fallback).with("develop").and_return("latest")
41
+ expect(subject.backing_service.tag).to eq("latest")
42
+ end
43
+
44
+ it "loads the configuration" do
45
+ expect(subject).to receive(:load_config).and_return({ "default" => { "test_command" => "foobar" }})
46
+ expect(subject.backing_service.test_command).to eq("foobar")
47
+ end
48
+ end
49
+
50
+ context "#load_config" do
51
+ it "uses the cache when available" do
52
+ configuration = double
53
+ allow(subject).to receive(:load_from_cache).and_return(configuration)
54
+ expect(subject.load_config).to eq(configuration)
55
+ end
56
+
57
+ it "loads configuration from the image when no cache is available" do
58
+ configuration = double
59
+ allow(subject).to receive(:load_from_cache).and_return(nil)
60
+ allow(subject).to receive(:load_from_image).and_return(configuration)
61
+ expect(subject.load_config).to eq(configuration)
62
+ end
63
+ end
64
+
65
+ context "#load_from_cache" do
66
+ let(:backing_service) { double(image_id: "123456789") }
67
+
68
+ before do
69
+ allow(subject).to receive(:backing_service).and_return(backing_service)
70
+ end
71
+
72
+ it "returns nil when no cache is found" do
73
+ expect(File).to receive(:exists?).with("tmp/configuration_cache/123456789").and_return(false)
74
+ expect(subject.load_from_cache).to eq(nil)
75
+ end
76
+
77
+ it "returns the configuration when a cache is found" do
78
+ expect(File).to receive(:exists?).with("tmp/configuration_cache/123456789").and_return(true)
79
+ expect(YAML).to receive(:load_file).with("tmp/configuration_cache/123456789").and_return({ "default" => { "image_name" => "foobar "}})
80
+ expect(subject.load_from_cache).to eq({ "default" => { "image_name" => "foobar "}})
81
+ end
82
+ end
83
+
84
+ context "#load_from_image" do
85
+ let(:backing_service) { double(full_image_name: "registry/application:develop", image_id: "123456789") }
86
+ let(:configuration) { double }
87
+
88
+ before do
89
+ allow(subject).to receive(:backing_service).and_return(backing_service)
90
+ end
91
+
92
+ it "reads the configuration from the image and caches the configuration" do
93
+ expect(Dockistrano::Docker).to receive(:run).with(backing_service.full_image_name, command: "cat /dockistrano.yml").and_return(raw_config = "---\ndefault:\n\tconfiguration: value")
94
+
95
+ expect(FileUtils).to receive(:mkdir_p).with("tmp/configuration_cache")
96
+ expect(File).to receive(:open).with("tmp/configuration_cache/#{backing_service.image_id}", "w+").and_return(file = double)
97
+ expect(file).to receive(:write).with(raw_config)
98
+ expect(file).to receive(:close)
99
+
100
+ expect(YAML).to receive(:load).with(raw_config).and_return(configuration)
101
+
102
+ expect(subject.load_from_image).to eq(configuration)
103
+ end
104
+
105
+ it "raises an error when host directories are missing" do
106
+ expect(Dockistrano::Docker).to receive(:run).with(backing_service.full_image_name, command: "cat /dockistrano.yml").and_return("No such file or directory: failed to mount")
107
+ expect { subject.load_from_image }.to raise_error(Dockistrano::ServiceDependency::HostDirectoriesMissing)
108
+ end
109
+
110
+ it "raises an error when the configuration is not found" do
111
+ expect(Dockistrano::Docker).to receive(:run).with(backing_service.full_image_name, command: "cat /dockistrano.yml").and_return("No such file or directory: dockistrano.yml")
112
+ expect { subject.load_from_image }.to raise_error(Dockistrano::ServiceDependency::ContainerConfigurationMissing)
113
+ end
114
+
115
+ it "raises an error when the configuration is empty" do
116
+ expect(Dockistrano::Docker).to receive(:run).with(backing_service.full_image_name, command: "cat /dockistrano.yml").and_return("")
117
+ expect { subject.load_from_image }.to raise_error(Dockistrano::ServiceDependency::ContainerConfigurationMissing)
118
+ end
119
+ end
120
+
121
+ context "#tag_with_fallback" do
122
+ let(:backing_service) { double(registry: "registry", image_name: "postgresql") }
123
+
124
+ before do
125
+ allow(subject).to receive(:backing_service).and_return(backing_service)
126
+ end
127
+
128
+ it "returns the given tag when the tag is available" do
129
+ expect(Dockistrano::Docker).to receive(:tags_for_image).and_return(["feature-branch", "develop", "master", "latest"])
130
+ expect(subject.tag_with_fallback("feature-branch")).to eq("feature-branch")
131
+ end
132
+
133
+ it "returns develop when the specific tag is not available" do
134
+ expect(Dockistrano::Docker).to receive(:tags_for_image).and_return(["develop", "master", "latest"])
135
+ expect(subject.tag_with_fallback("feature-branch")).to eq("develop")
136
+ end
137
+
138
+ it "returns master when develop is not available" do
139
+ expect(Dockistrano::Docker).to receive(:tags_for_image).and_return(["master", "latest"])
140
+ expect(subject.tag_with_fallback("feature-branch")).to eq("master")
141
+ end
142
+
143
+ it "returns latest when master is not available" do
144
+ expect(Dockistrano::Docker).to receive(:tags_for_image).and_return(["latest"])
145
+ expect(subject.tag_with_fallback("feature-branch")).to eq("latest")
146
+ end
147
+
148
+ it "raises an error when not appropriate tag is found" do
149
+ expect(Dockistrano::Docker).to receive(:tags_for_image).and_return(["another-feature-branch"])
150
+ expect { subject.tag_with_fallback("feature-branch") }.to raise_error(Dockistrano::ServiceDependency::NoTagFoundForImage)
151
+ end
152
+ end
153
+
154
+ end