Skip to content

Commit 52eb386

Browse files
ETiVETiV Wang
and
ETiV Wang
authored
Avoid fetching user-data when fetching meta-data on Alibaba Cloud (#1802)
Signed-off-by: ETiV Wang <et@xde.com> Co-authored-by: ETiV Wang <yufan@xde.com>
1 parent c756b22 commit 52eb386

File tree

3 files changed

+204
-12
lines changed

3 files changed

+204
-12
lines changed

lib/ohai/mixin/alibaba_metadata.rb

+15-10
Original file line numberDiff line numberDiff line change
@@ -38,22 +38,27 @@ def http_get(uri)
3838
conn.get("/2016-01-01/#{uri}", { "User-Agent" => "chef-ohai/#{Ohai::VERSION}" })
3939
end
4040

41-
def fetch_metadata(id = "")
41+
def fetch_metadata(id = "", is_directory = true)
4242
response = http_get(id)
4343
return nil unless response.code == "200"
4444

45-
if json?(response.body)
46-
data = String(response.body)
47-
parser = FFI_Yajl::Parser.new
48-
parser.parse(data)
49-
elsif response.body.include?("\n")
45+
if !is_directory
46+
if json?(response.body)
47+
data = String(response.body)
48+
parser = FFI_Yajl::Parser.new
49+
parser.parse(data)
50+
else
51+
response.body
52+
end
53+
elsif is_directory
5054
temp = {}
5155
response.body.split("\n").each do |sub_attr|
52-
temp[sanitize_key(sub_attr)] = fetch_metadata("#{id}/#{sub_attr}")
56+
if "#{id}/#{sub_attr}" != "/user-data"
57+
uri = id == "" ? "#{id}#{sub_attr}/" : "#{id}#{sub_attr}"
58+
temp[sanitize_key(sub_attr).gsub(/_$/, "")] = fetch_metadata(uri, has_trailing_slash?(uri))
59+
end
5360
end
5461
temp
55-
else
56-
response.body
5762
end
5863
end
5964

@@ -75,7 +80,7 @@ def json?(data)
7580
#
7681
# @return [Boolean] is there a trailing /?
7782
def has_trailing_slash?(data)
78-
!!( data =~ %r{/$} )
83+
!!(data =~ %r{/$})
7984
end
8085

8186
def sanitize_key(key)
+187
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
#
2+
# Author:: ETiV Wang <et@xde.com>
3+
# Copyright:: Copyright (c) Chef Software Inc.
4+
# License:: Apache License, Version 2.0
5+
#
6+
# Licensed under the Apache License, Version 2.0 (the "License");
7+
# you may not use this file except in compliance with the License.
8+
# You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing, software
13+
# distributed under the License is distributed on an "AS IS" BASIS,
14+
# WITHOUT WARRANTIES OR CONDIT"Net::HTTP Response"NS OF ANY KIND, either express or implied.
15+
# See the License for the specific language governing permissions and
16+
# limitations under the License.
17+
#
18+
require "spec_helper"
19+
require "ohai/mixin/alibaba_metadata"
20+
21+
describe Ohai::Mixin::AlibabaMetadata do
22+
let(:mixin) do
23+
metadata_object = Object.new.extend(described_class)
24+
conn = double("Net::HTTP client")
25+
allow(conn).to receive(:get).and_return(response)
26+
allow(metadata_object).to receive(:http_get).and_return(conn.get)
27+
metadata_object
28+
end
29+
30+
before do
31+
logger = instance_double("Mixlib::Log::Child", trace: nil, debug: nil, warn: nil)
32+
allow(mixin).to receive(:logger).and_return(logger)
33+
end
34+
35+
JSON_STR = %{{"zone-id":"cn-shanghai-a","region-id":"cn-shanghai","private-ipv4":"192.168.0.200","instance-type":"ecs.4xlarge"}}.freeze
36+
CONF_STR = %{#cloud-config
37+
# system wide
38+
timezone: Asia/Shanghai
39+
locale: en_US.UTF-8
40+
manage_etc_hosts: localhost
41+
## apt & packages
42+
apt:
43+
disable_suites: [proposed, $RELEASE-proposed-updates]
44+
package_update: true
45+
package_upgrade: true
46+
packages:
47+
- htop
48+
- lvm2
49+
- tmux
50+
- dnsutils
51+
- net-tools
52+
- rsync
53+
- ca-certificates
54+
- curl
55+
}.freeze
56+
57+
API_TREE = {
58+
"meta-data" => {
59+
# plain K-V
60+
"a" => "b",
61+
# nested structure
62+
"c" => {
63+
"d" => "e",
64+
},
65+
# has a new-line but not nested
66+
"dns-conf" => "1.1.1.1\n1.0.0.1",
67+
"eipv4" => nil,
68+
"private_ipv4" => "192.168.2.1",
69+
"private_ipv4s" => ["192.168.2.2"],
70+
"hostname" => "some-host.example.com",
71+
},
72+
"json-data" => {
73+
"dynamic" => JSON_STR,
74+
},
75+
"user-data" => CONF_STR,
76+
}.freeze
77+
78+
def compare_tree(local, remote, need_sanitize = false)
79+
local.all? do |k, v|
80+
key = k
81+
key = mixin.sanitize_key(k) if !!need_sanitize
82+
83+
if v.class == Hash
84+
return compare_tree(v, remote[key], need_sanitize)
85+
else
86+
return v == remote[key]
87+
end
88+
end
89+
end
90+
91+
describe "#fetch_metadata" do
92+
context "when get a non-200 status code" do
93+
let(:response) { double("Net::HTTP Response", code: "404") }
94+
95+
it "should get nil" do
96+
expect(mixin.fetch_metadata).to eq(nil)
97+
end
98+
end
99+
100+
context "when get a plain text content without new-line" do
101+
let(:response) { double("Net::HTTP Response", body: "bar", code: "200") }
102+
103+
it "should be its original content" do
104+
expect(mixin.fetch_metadata("foo", false)).to eq("bar")
105+
end
106+
end
107+
108+
context "when get a plain text content with a new-line" do
109+
let(:response) { double("Net::HTTP Response", body: "bar\nbaz", code: "200") }
110+
111+
it "should be its original content" do
112+
expect(mixin.fetch_metadata("foo", false)).to eq("bar\nbaz")
113+
end
114+
end
115+
116+
context "when get a JSON response" do
117+
let(:response) { double("Net::HTTP Response", body: JSON_STR, code: "200") }
118+
119+
it "should be parsed" do
120+
ret = mixin.fetch_metadata("foo", false)
121+
122+
parser = FFI_Yajl::Parser.new
123+
json_obj = parser.parse(JSON_STR)
124+
125+
expect(compare_tree(json_obj, ret, false)).to eq(true)
126+
end
127+
end
128+
129+
context "when recursively fetching a tree structure from root" do
130+
let(:response) { double("Net::HTTP Response", body: "", code: "200") }
131+
132+
it "should be a nested structure" do
133+
allow(mixin).to receive(:http_get) do |uri|
134+
tree = API_TREE
135+
136+
uri.split("/").each do |part|
137+
tree = tree[part] unless part == ""
138+
end
139+
140+
output = [tree]
141+
if tree.class == Hash
142+
output = tree.keys.map do |key|
143+
ret = key
144+
ret += "/" if tree[key].class == Hash
145+
ret
146+
end
147+
end
148+
149+
double("Net::HTTP Response", body: output.join("\n"), code: "200")
150+
end
151+
152+
ret = mixin.fetch_metadata
153+
154+
expect(compare_tree(API_TREE, ret, true)).to eq(true)
155+
end
156+
157+
it "should avoid the key user-data" do
158+
allow(mixin).to receive(:http_get) do |uri|
159+
tree = API_TREE
160+
161+
uri.split("/").each do |part|
162+
tree = tree[part] unless part == ""
163+
end
164+
165+
output = [tree]
166+
if tree.class == Hash
167+
output = tree.keys.map do |key|
168+
ret = key
169+
ret += "/" if tree[key].class == Hash
170+
ret
171+
end
172+
end
173+
174+
double("Net::HTTP Response", body: output.join("\n"), code: "200")
175+
end
176+
177+
ret = mixin.fetch_metadata
178+
179+
expect(ret["meta_data"]).not_to be_nil
180+
expect(ret["json_data"]).not_to be_nil
181+
expect(ret["user_data"]).to eq(nil)
182+
183+
end
184+
end
185+
186+
end
187+
end

spec/unit/plugins/alibaba_spec.rb

+2-2
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@
4343
before do
4444
@http_get = double("Net::HTTP client")
4545
allow(plugin).to receive(:http_get).with("").and_return(double("Net::HTTP Response", body: "meta-data\n", code: "200"))
46-
allow(plugin).to receive(:http_get).with("/meta-data").and_return(double("Net::HTTP Response", body: "hostname\n", code: "200"))
47-
allow(plugin).to receive(:http_get).with("/meta-data/hostname").and_return(double("Net::HTTP Response", body: "foo", code: "200"))
46+
allow(plugin).to receive(:http_get).with("meta-data/").and_return(double("Net::HTTP Response", body: "hostname\n", code: "200"))
47+
allow(plugin).to receive(:http_get).with("meta-data/hostname").and_return(double("Net::HTTP Response", body: "foo", code: "200"))
4848
allow(IO).to receive(:select).and_return([[], [1], []])
4949
t = double("connection")
5050
allow(t).to receive(:connect_nonblock).and_raise(Errno::EINPROGRESS)

0 commit comments

Comments
 (0)