1 % ============LICENSE_START=======================================================
3 % ================================================================================
4 % Copyright (c) 2017 AT&T Intellectual Property. All rights reserved.
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
10 % http://www.apache.org/licenses/LICENSE-2.0
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 CONDITIONS OF ANY KIND, either express or implied.
15 % See the License for the specific language governing permissions and
16 % limitations under the License.
17 % ============LICENSE_END=========================================================
19 % ECOMP is a trademark and service mark of AT&T Intellectual Property.
20 -module(apitest_SUITE).
21 -include_lib("common_test/include/ct.hrl").
22 -include("../../src/application.hrl").
23 -export([all/0, groups/0, init_per_suite/1, end_per_suite/1]).
24 -export([server_health_test/1, app_deploy/1, hydrator_deploy/1, app_teardown/1, app_test/1, app_reconfigure/1, test_failures/1, app_botch_flows/1, app_botch_delete/1, app_botch_consul_delete/1, delete_all/1,
25 hydrator_app_teardown/1, hydrator_test/1,
26 hydrator_wdeps_deploy/1,
27 hydrator_wdeps_test/1,
28 hydrator_wdeps_teardown/1
31 %lazy shorthands (yay C style macros! miss these in python)
32 -define(SC(L), util:concat(L)).
33 -define(PLG(K, PL), proplists:get_value(K, PL)).
34 -define(XER, "testing-XER").
35 -define(D(X), erlang:display(X)).
40 {group, apibotchedflows},
41 {group, apibotcheddeleted},
42 {group, apibotchedconsuldeleted},
46 {progapi, %prog-flow test
57 {hydratorapi, %hydrator test
64 hydrator_app_teardown,
65 hydrator_wdeps_deploy,
67 hydrator_wdeps_teardown
69 {apibotchedflows, %deploy, manually stop flows, then try to delete
77 {apibotcheddeleted, %deploy, manually stop flows, delete it manually from cdap, then try to delete
85 {apibotchedconsuldeleted, %deploy, manually stop flows, delete it manually from cdap, then try to delete
90 app_botch_consul_delete,
103 setup_rels(Config, D) ->
104 %deploy/delete the testing keys into Consul. This would normally be done by the Cloudify plugin
106 %#NOTE: This is weird. The sequence of steps is:
107 % 1 Cloudify populates rels key
108 % 2 Cloudify sends broker the unbound config
109 % 3 Broker pushes unbound config to consul
110 % 4 Broker binds config
111 % 5 Broker pushes bound config to CDAP
112 % Between state 1 and 3 consul is in an inconsistent state where it has only the rels key but not the config key. Not so sure about this. They seem to be a pair. Maybe the rels key should be pushed to the source node to be dealt with.
113 % #Here, we are mocking step 1
114 URL = ?SC([?PLG(consul_url, Config), "/v1/kv/", ?PLG(appname, Config), ":rel"]),
116 setup -> {200,"true"} = httpabs:put(?XER, URL, "application/json", jiffy:encode([<<"666_fake_testing_service">>]));
117 teardown -> {200,"true"} = httpabs:delete(?XER, URL)
119 httpabs:get(?XER, URL).
121 setup_fake_testing_service(Config, D) ->
122 %register a fake testing service to test that the CDAP app recieved it's bound configuration properly
123 Name = <<"666_fake_testing_service">>,
124 SrvURL = ?SC([?PLG(consul_url, Config), "/v1/catalog/service/", Name]),
127 URL = ?SC([?PLG(consul_url, Config), "/v1/agent/service/register"]),
128 Body = {[{<<"name">>, Name},
129 {<<"Address">>, <<"666.666.666.666">>},
132 {200, []} = httpabs:put(?XER, URL, "application/json", jiffy:encode(Body)),
133 httpabs:get(?XER, SrvURL);
135 %total failure on Consul's part for this not to be a delete on the same endpoint
136 URL = ?SC([?PLG(consul_url, Config), "/v1/agent/service/deregister/", Name]),
137 {200, []} = httpabs:put(?XER, URL, "application/json", ""),
138 httpabs:get(?XER, SrvURL)
141 get_config_consul(C) ->
142 %get config from consul. returns the code too for tests testing for a 404
143 {RC, RB} = consul_interface:consul_get_configuration(?XER, ?PLG(appname, C), ?PLG(consul_url, C)),
145 200 -> {RC, util:ejson_to_map(RB)};
149 get_config_cdap(C) ->
150 {RC, RB} = cdap_interface:get_app_config(?XER, ?PLG(appname, C), ?PLG(namespace, C),?PLG(cdap_url, C)),
153 %I think CDAP is DOUBLY encoding JSON!!
154 {RC, jiffy:decode(jiffy:decode(jiffy:encode(RB)), [return_maps])};
158 get_preferences_cdap(C) ->
159 {RC, RB} = cdap_interface:get_app_preferences(?XER, ?PLG(appname, C), ?PLG(namespace, C),?PLG(cdap_url, C)),
161 200 -> {RC, util:ejson_to_map(RB)};
165 get_preferences_consul(C) ->
166 %get preferences from consul. returns the code too for tests testing for a 404
167 {RC, RB} = consul_interface:consul_get_preferences(?XER, ?PLG(appname, C), ?PLG(consul_url, C)),
169 200 -> {RC, util:ejson_to_map(RB)};
173 valid_deploy_body(C) ->
175 {<<"cdap_application_type">>, <<"program-flowlet">>},
176 {<<"namespace">>, ?PLG(namespace, C)},
177 {<<"streamname">>, ?PLG(streamname, C)},
178 {<<"jar_url">>, ?PLG(jar_url, C)},
179 {<<"artifact_name">>, ?PLG(art_name, C)},
180 {<<"artifact_version">>, ?PLG(art_ver, C)},
181 {<<"app_config">>, ?PLG(init_config, C)},
182 {<<"app_preferences">>, ?PLG(init_preferences, C)},
183 {<<"services">>, [{[{<<"service_name">>, <<"Greeting">>},
184 {<<"service_endpoint">>, <<"greet">>},
185 {<<"endpoint_method">>, <<"GET">>}]}]},
187 {[{<<"program_type">>, <<"flows">>},
188 {<<"program_id">>, <<"WhoFlow">>}]},
189 {[{<<"program_type">>, <<"services">>},
190 {<<"program_id">>, <<"Greeting">>}]}]},
191 {<<"program_preferences">>, [
192 {[{<<"program_type">>,<<"flows">>},
193 {<<"program_id">>, <<"WhoFlow">>},
194 {<<"program_pref">>, ?PLG(whoflowpref, C)}]}
200 init_per_suite(_C) ->
202 [MyName, ConsulURL, _, _] = util:get_platform_envs_and_config(),
204 BrokerUrl = case os:getenv("BROKER_TEST_TYPE") of
205 false -> %no env variable means start the broker on localhost
206 %start a local broker
207 {ok,[syntax_tools,compiler,goldrush,lager,jiffy,mnesia,ranch,cowlib,cowboy,leptus,uuid,iso8601,cdapbroker]} = application:ensure_all_started(cdapbroker),
208 "http://localhost:7777";
210 "http://localhost:7777";
212 %Using MyName, fetch from Consul the broker info
213 {MyIP, MyPort} = consul_interface:consul_get_service_ip_port(MyName, ConsulURL),
214 ?SC(["http://", MyIP, ":", integer_to_binary(MyPort)])
217 %get NEXUS_ROOT for testing purposes.
218 Nexus = os:getenv("NEXUS_RAW_ROOT"),
219 true = (Nexus /= false), %blow if this wasn't set, we need it.
221 {200, RB} = httpabs:get(?XER, BrokerUrl),
222 CDAPUrl = maps:get(<<"managed cdap url">>, jiffy:decode(RB, [return_maps])),
224 %set properties that are shared between program-flowlet and hydrator
225 Namespace = <<"testns">>,
226 CDAPUrlNS = ?SC([CDAPUrl, "/v3/namespaces/", Namespace]),
228 %set up config for program-flowlet app
229 Appname = <<"hwtest">>,
230 Streamname = <<"who">>,
232 %setup config for hydrator pipeline
233 HydratorAppname = <<"hydratortest">>,
234 HydratorAppURL = ?SC([CDAPUrlNS, "/apps/", HydratorAppname]),
235 HydratorStreamname = <<"s1">>, %horrible name but not made by me
237 HydratorWDepsAppname = <<"hydratorwdepstest">>,
238 HydratorWDepsAppURL = ?SC([CDAPUrlNS, "/apps/", HydratorWDepsAppname]),
239 HydratorWDepsStreamname = <<"t1">>, %horrible name but not made by me
241 %Set up this test suites configuration
242 [{broker_url, BrokerUrl},
244 {cdap_ns_url, CDAPUrlNS},
245 {jar_url, ?SC([Nexus, "/jar_files/HelloWorld-3.4.3.jar"])},
246 {consul_url, ConsulURL},
247 {consul_app_url, ?SC([ConsulURL, "/v1/catalog/service/", Appname])},
248 {app_url, ?SC([CDAPUrlNS, "/apps/", Appname])},
249 {namespace, Namespace},
251 {broker_app_url, ?SC([BrokerUrl, "/application/", Appname])},
252 {stream_url, ?SC([CDAPUrlNS, "/streams/", Streamname])},
253 {art_ver, <<"3.4.3">>},
254 {art_name, <<"HelloWorld">>},
255 {streamname, Streamname},
257 {<<"streams_produces">>, <<"\{\{fake_testing_service\}\}">>},
258 {<<"services_calls">>, <<"\{\{fake_testing_service\}\}">>},
259 {<<"donotresolveme">>, <<"donotabsolveme">>}
261 {whoflowpref, {[{<<"progfoo">>, <<"progbar">>}]}},
262 {init_preferences, {[{<<"preffoo">>, <<"prefbar">>}]}},
263 {reconfig, {[{<<"foo">>, <<"bar">>}]}},
265 %hydrator test properties
266 {hydrator_appname, HydratorAppname},
267 {broker_hydrator_app_url, ?SC([BrokerUrl, "/application/", HydratorAppname])},
268 {hydrator_app_url, HydratorAppURL},
269 {hydrator_json_url, ?SC([Nexus, "/json_files/t1-4.1.2.json "])},
270 {hydrator_pipeline_status_url, ?SC([HydratorAppURL, "/schedules/dataPipelineSchedule/status"])},
271 {hydrator_stream_url, ?SC([CDAPUrlNS, "/streams/", HydratorStreamname])},
272 {hydrator_streamname, HydratorStreamname},
273 {consul_hydrator_app_url, ?SC([ConsulURL, "/v1/catalog/service/", HydratorAppname])},
275 %hydrator with deps test properties
276 {hydrator_wdeps_appname, HydratorWDepsAppname},
277 {hydrator_wdeps_artname, <<"demoTCA">>},
278 {hydrator_wdeps_artver, <<"1.0.0-SNAPSHOT">>},
279 {hydrator_wdeps_app_url, HydratorWDepsAppURL},
280 {broker_hydrator_wdeps_app_url, ?SC([BrokerUrl, "/application/", HydratorWDepsAppname])},
281 {hydrator_wdeps_streamname, HydratorWDepsStreamname},
282 {hydrator_wdeps_stream_url, ?SC([CDAPUrlNS, "/streams/", HydratorWDepsStreamname])},
283 {hydrator_wdeps_json_url, ?SC([Nexus, "/json_files/t1-4.1.2.json"])},
284 {hydrator_wdeps_properties_json_url, ?SC([Nexus, "/json_files/demoTCA-1.0.0-SNAPSHOT-properties.json"])},
285 {hydrator_wdeps_jar_url, ?SC([Nexus, "/json_files/demoTCA-1.0.0-SNAPSHOT.jar"])},
286 {hydrator_wdeps_test_data_url, ?SC([Nexus, "/txt_files/tcaDemoData100k.txt"])},
287 {consul_hydrator_wdeps_app_url, ?SC([ConsulURL, "/v1/catalog/service/", HydratorWDepsAppname])},
288 {hydrator_pipeline_wdeps_status_url, ?SC([HydratorWDepsAppURL, "/schedules/dataPipelineSchedule/status"])}
293 _ = application:stop(cdapbroker).
295 server_health_test(C) ->
296 {200, RB} = httpabs:get(?XER, ?PLG(broker_url,C)),
297 M = jiffy:decode(RB, [return_maps]),
298 true = maps:is_key(<<"managed cdap url">>, M),
299 true = maps:is_key(<<"number of applications registered">>, M),
300 true = maps:is_key(<<"uptime (s)">>, M),
301 true = maps:is_key(<<"cdap cluster version">>, M),
302 true = maps:is_key(<<"cdap GUI port">>, M),
303 true = maps:is_key(<<"broker API version">>, M)
306 app_deploy(C) -> %C == Config
307 %Deploy the test application
310 {200, _} = setup_rels(C, setup),
312 %Register the fake testing service to test config binding. Make sure it's not empty
313 {200, F} = setup_fake_testing_service(C, setup),
316 ExpectedBoundConfg = maps:from_list([
317 {<<"services_calls">> , [<<"666.666.666.666:13">>]},
318 {<<"streams_produces">>, [<<"666.666.666.666:13">>]},
319 {<<"donotresolveme">> , <<"donotabsolveme">>}
322 %Maps can be used safely with the == operator but appears proplists cannot be
323 Expected = maps:from_list([
324 {<<"appname">>, ?PLG(appname, C)},
325 {<<"apptype">>, <<"program-flowlet">>},
326 {<<"namespace">>, ?PLG(namespace, C)},
327 {<<"healthcheckurl">>, list_to_binary(?SC([?PLG(broker_app_url, C), "/healthcheck"]))},
328 {<<"metricsurl">>, list_to_binary(?SC([?PLG(broker_app_url, C), "/metrics"]))},
329 {<<"url">>, list_to_binary(?PLG(broker_app_url, C))},
330 {<<"connectionurl">>, list_to_binary(?PLG(stream_url, C))},
331 {<<"serviceendpoints">>, [#{<<"url">> => list_to_binary(?SC([?PLG(app_url, C), "/services/Greeting/methods/greet"])),
332 <<"method">> => <<"GET">>}]},
333 {<<"unbound_config">>, #{<<"streams_produces">> => <<"\{\{fake_testing_service\}\}">>, <<"services_calls">> => <<"\{\{fake_testing_service\}\}">>, <<"donotresolveme">> => <<"donotabsolveme">>}},
334 {<<"bound_config">>, ExpectedBoundConfg}
337 %assert the current appliccation list does not contain our test app
338 {200,RB0} = httpabs:get(?XER, ?SC([?PLG(broker_url, C), "/application"])),
339 true = lists:all(fun(X) -> X /= ?PLG(appname, C) end, jiffy:decode(RB0)),
342 Body = valid_deploy_body(C),
343 {200, RB} = httpabs:put(?XER, ?PLG(broker_app_url, C), "application/json", jiffy:encode(Body)),
345 %The CDAP APIs return the config as a JSON dumped to a string, so we need to get that back into a real JSON to have key-order-independent equality testing
347 RBMap = jiffy:decode(X, [return_maps]),
348 maps:update(<<"bound_config">>, jiffy:decode(maps:get(<<"bound_config">>, RBMap), [return_maps]), RBMap)
351 %assert that the return and get matches what we put in
352 true = Fix(RB) == Expected,
354 %assert hitting the get application endpoint works
355 {200, RB2} = httpabs:get(?XER, ?PLG(broker_app_url, C)),
356 true = Fix(RB2) == Expected,
358 %assert the current application list now includes our new app
359 {200, RB3} = httpabs:get(?XER, ?SC([?PLG(broker_url, C), "/application"])),
360 true = lists:any(fun(X) -> X == ?PLG(appname, C) end, jiffy:decode(RB3)),
362 %make sure it is in CDAP
363 {200, _} = httpabs:get(?XER, ?PLG(app_url, C)),
366 {200, _} = httpabs:get(?XER, ?SC([?PLG(broker_app_url, C), "/metrics"])),
369 {200, _} = httpabs:get(?XER,?SC([?PLG(broker_app_url, C), "/healthcheck"])),
371 %make sure that the service is registered. TODO! Could get more fancy by manually checking a healthcheck
372 {200, RBHC} = httpabs:get(?XER,?PLG(consul_app_url, C)),
373 true = jiffy:decode(RBHC) /= [],
375 %check that the UNbound config is correct
376 true = {200, util:ejson_to_map(?PLG(init_config, C))} == get_config_consul(C),
378 %check that the preferences in Consul is correct
379 InitPrefMap = util:ejson_to_map(?PLG(init_preferences, C)),
380 true = {200, InitPrefMap} == get_preferences_consul(C),
382 %make sure CDAP has right preferences
383 true = {200, InitPrefMap} == get_preferences_cdap(C),
385 %make sure the config binding service and pulling config out of CDAP all match
386 %> get it strait from CBS
387 CBSUrl = util:resolve_cbs(?XER, ?PLG(consul_url, C)),
388 {200, RB4} = httpabs:get(?XER, ?SC([CBSUrl, "/service_component/", ?PLG(appname, C)])),
390 {200, CDAPConfig} = get_config_cdap(C),
391 %make sure everythng is as expected
392 true = ExpectedBoundConfg == jiffy:decode(RB4, [return_maps]),
393 true = ExpectedBoundConfg == CDAPConfig,
395 %try to put the same app again and assert you get a 400
396 {400,"State: Bad Request. Return Body: Put recieved on /application/:appname but appname is already registered. Call /application/:appname/reconfigure if trying to reconfigure or delete first"} =
397 httpabs:put(?XER, ?PLG(broker_app_url, C), "application/json", jiffy:encode(Body)).
399 hydrator_deploy(C) ->
401 {<<"cdap_application_type">>, <<"hydrator-pipeline">>},
402 {<<"namespace">>, ?PLG(namespace, C)},
403 {<<"streamname">>, ?PLG(hydrator_streamname, C)},
404 {<<"pipeline_config_json_url">>, ?PLG(hydrator_json_url, C)}
406 Expected = maps:from_list([
407 {<<"appname">>, ?PLG(hydrator_appname, C)},
408 {<<"apptype">>, <<"hydrator-pipeline">>},
409 {<<"namespace">>, ?PLG(namespace, C)},
410 {<<"healthcheckurl">>, list_to_binary(?SC([?PLG(broker_hydrator_app_url, C), "/healthcheck"]))},
411 {<<"metricsurl">>, list_to_binary(?SC([?PLG(broker_hydrator_app_url, C), "/metrics"]))},
412 {<<"url">>, list_to_binary(?PLG(broker_hydrator_app_url, C))},
413 {<<"connectionurl">>, list_to_binary(?PLG(hydrator_stream_url, C))},
414 {<<"serviceendpoints">>, []}
417 %assert the current appliccation list does not contain our test app
418 {200,RB0} = httpabs:get(?XER, ?SC([?PLG(broker_url, C), "/application"])),
419 true = lists:all(fun(X) -> X /= ?PLG(hydrator_appname, C) end, jiffy:decode(RB0)),
422 {200, RB1} = httpabs:put(?XER, ?PLG(broker_hydrator_app_url, C), "application/json", jiffy:encode(Body)),
423 true = jiffy:decode(RB1, [return_maps]) == Expected,
425 %make sure the Execution resume worked
426 {200, RB2} = httpabs:get(?XER, ?PLG(hydrator_pipeline_status_url, C)),
427 true = jiffy:decode(RB2) == {[{<<"status">>, <<"SCHEDULED">>}]},
429 %make sure it is in CDAP
430 {200, _} = httpabs:get(?XER, ?PLG(hydrator_app_url, C)),
432 %assert the current application list now includes our new app
433 {200, RB3} = httpabs:get(?XER, ?SC([?PLG(broker_url, C), "/application"])),
434 true = lists:any(fun(X) -> X == ?PLG(hydrator_appname, C) end, jiffy:decode(RB3)),
437 {200, _} = httpabs:get(?XER,?SC([?PLG(broker_hydrator_app_url, C), "/healthcheck"])),
440 {200, _} = httpabs:get(?XER,?SC([?PLG(broker_hydrator_app_url, C), "/metrics"])),
442 %make sure that the service is registered. TODO! Could get more fancy by manually checking a healthcheck
443 {200, RBHC} = httpabs:get(?XER,?PLG(consul_hydrator_app_url, C)),
444 true = jiffy:decode(RBHC) /= []
447 hydrator_wdeps_deploy(C) ->
449 {<<"cdap_application_type">>, <<"hydrator-pipeline">>},
450 {<<"namespace">>, ?PLG(namespace, C)},
451 {<<"streamname">>, ?PLG(hydrator_wdeps_streamname, C)},
452 {<<"pipeline_config_json_url">>, ?PLG(hydrator_wdeps_json_url, C)},
453 {<<"dependencies">>, [
455 {<<"artifact_extends_header">>, <<"system:cdap-data-pipeline[4.1.0,5.0.0)">>},
456 {<<"artifact_name">>, ?PLG(hydrator_wdeps_artname, C)},
457 {<<"artifact_version_header">>, ?PLG(hydrator_wdeps_artver, C)},
458 {<<"artifact_url">>, ?PLG(hydrator_wdeps_jar_url, C)},
459 {<<"ui_properties_url">>, ?PLG(hydrator_wdeps_properties_json_url, C)}
463 Expected = maps:from_list([
464 {<<"appname">>, ?PLG(hydrator_wdeps_appname, C)},
465 {<<"apptype">>, <<"hydrator-pipeline">>},
466 {<<"namespace">>, ?PLG(namespace, C)},
467 {<<"healthcheckurl">>, list_to_binary(?SC([?PLG(broker_hydrator_wdeps_app_url, C), "/healthcheck"]))},
468 {<<"metricsurl">>, list_to_binary(?SC([?PLG(broker_hydrator_wdeps_app_url, C), "/metrics"]))},
469 {<<"url">>, list_to_binary(?PLG(broker_hydrator_wdeps_app_url, C))},
470 {<<"connectionurl">>, list_to_binary(?PLG(hydrator_wdeps_stream_url, C))},
471 {<<"serviceendpoints">>, []}
473 %assert the current appliccation list does not contain our test app
474 {200,RB0} = httpabs:get(?XER,?SC([?PLG(broker_url, C), "/application"])),
475 true = lists:all(fun(X) -> X /= ?PLG(hydrator_wdeps_appname, C) end, jiffy:decode(RB0)),
478 {200, RB1} = httpabs:put(?XER, ?PLG(broker_hydrator_wdeps_app_url, C), "application/json", jiffy:encode(Body)),
479 true = jiffy:decode(RB1, [return_maps]) == Expected,
481 %make sure properties are loaded, test artifact
482 {200, _} = httpabs:get(?XER,?SC([?PLG(cdap_ns_url, C), "/artifacts/", ?PLG(hydrator_wdeps_artname, C), "/versions/", ?PLG(hydrator_wdeps_artver, C), "/properties"])),
484 %make sure the Execution resume worked
485 {200, RB2} = httpabs:get(?XER,?PLG(hydrator_pipeline_wdeps_status_url, C)),
486 true = jiffy:decode(RB2) == {[{<<"status">>, <<"SCHEDULED">>}]},
488 %make sure it is in CDAP
489 {200, _} = httpabs:get(?XER,?PLG(hydrator_wdeps_app_url, C)),
491 %assert the current application list now includes our new app
492 {200, RB3} = httpabs:get(?XER,?SC([?PLG(broker_url, C), "/application"])),
493 true = lists:any(fun(X) -> X == ?PLG(hydrator_wdeps_appname, C) end, jiffy:decode(RB3)),
496 {200, _} = httpabs:get(?XER,?SC([?PLG(broker_hydrator_wdeps_app_url, C), "/healthcheck"])),
499 {200, _} = httpabs:get(?XER,?SC([?PLG(broker_hydrator_wdeps_app_url, C), "/metrics"])),
501 %make sure that the service is registered. TODO! Could get more fancy by manually checking a healthcheck
502 {200, RBHC} = httpabs:get(?XER,?PLG(consul_hydrator_wdeps_app_url, C)),
503 true = jiffy:decode(RBHC) /= []
507 %test te app by injecting some data into the stream and getting it out
508 %Sleeping since HTTP services may still be booting up: see https://issues.cask.co/browse/CDAP-812
509 ok = timer:sleep(60000), %60s
511 {200, _} = httpabs:post(?XER, ?PLG(hydrator_stream_url, C), "text/plain", "beer, vodka, gin"),
513 PB = jiffy:encode({[{<<"query">>, <<"select v1, v2, v3 from dataset_pf1">>}]}),
514 {200, RB} = httpabs:post(?XER, ?SC([?PLG(cdap_ns_url, C), "/data/explore/queries"]), "text/plain", PB),
515 {[{<<"handle">>, Handle}]} = jiffy:decode(RB),
516 %results can take time, sleep again
517 ok = timer:sleep(30000),
519 {<<"status">>,<<"FINISHED">>},
520 {<<"hasResults">>,true}
522 {200, RB2} = httpabs:get(?XER, ?SC([?PLG(cdap_url, C), "/v3/data/explore/queries", "/", Handle, "/status"])),
523 true = Expected == jiffy:decode(RB2),
524 {200, _} = httpabs:post(?XER, ?SC([?PLG(cdap_url, C), "/v3/data/explore/queries", "/", Handle, "/next"]), "text/plain", "")
528 %Sleeping since HTTP services may still be booting up: see https://issues.cask.co/browse/CDAP-812
529 ok = timer:sleep(60000), %60s
530 {200, _} = httpabs:post(?XER, ?PLG(stream_url, C), "text/plain", "'Prince of Darkness'"),
531 {200, "Hello 'Prince of Darkness'!"} = httpabs:get(?XER,?SC([?PLG(app_url, C), "/services/Greeting/methods/greet"])).
533 app_reconfigure(C) ->
534 %Test app reconfiguration
535 %test new config right in Consul
536 true = {200, util:ejson_to_map(?PLG(init_config, C))} == get_config_consul(C),
539 ReconfigMap = util:ejson_to_map({[{<<"foo REDUX EDITION">>, <<"bar">>}, {<<"LEAVE ME ALONE">>, <<"CONFIG EDITION">>}]}),
541 {200, _} = httpabs:put(?XER, ?SC([?PLG(broker_app_url, C), "/reconfigure"]), "application/json", jiffy:encode({[{<<"reconfiguration_type">>, <<"program-flowlet-app-config">>},{<<"config">>, ReconfigMap}]})),
542 %test new config right in consul
543 true = {200, ReconfigMap} == get_config_consul(C),
544 %test new config right in cdap
545 true = {200, ReconfigMap} == get_config_cdap(C),
547 %Test preferences reconfiguration
548 %check that the preferences in Consul is correct
549 InitMap = util:ejson_to_map(?PLG(init_preferences, C)),
550 true = {200, InitMap} == get_preferences_consul(C),
551 %check that the preferences in CDAP are correct
552 true = {200, InitMap} == get_preferences_cdap(C),
553 %reconfigure the preferences
554 PreferencesReconfigMap = util:ejson_to_map({[{<<"preffoo REDUX EDITION">>, <<"prefbar REMIXXX">>}, {<<"LEAVE ME ALONE">>, <<"PREFERENCES EDITION">>}]}),
555 {200, _} = httpabs:put(?XER, ?SC([?PLG(broker_app_url, C), "/reconfigure"]), "application/json", jiffy:encode({[{<<"reconfiguration_type">>, <<"program-flowlet-app-preferences">>},{<<"config">>, PreferencesReconfigMap}]})),
556 %make sure consul has right preferences
557 true = {200, PreferencesReconfigMap} == get_preferences_consul(C),
558 %make sure CDAP has right preferences
559 true = {200, PreferencesReconfigMap} == get_preferences_cdap(C),
561 %test the smart reconfiguration call
562 %try to give it a smart where there are keys in just preferences
563 SmartReconfigPrefMap = util:ejson_to_map({[{<<"preffoo REDUX EDITION">>, <<"BAR'D AGAIN">>}]}),
564 {200, _} = httpabs:put(?XER, ?SC([?PLG(broker_app_url, C), "/reconfigure"]), "application/json", jiffy:encode({[{<<"reconfiguration_type">>, <<"program-flowlet-smart">>},{<<"config">>, SmartReconfigPrefMap}]})),
565 ExpectedNewPreferences = #{<<"LEAVE ME ALONE">>=><<"PREFERENCES EDITION">>,<<"preffoo REDUX EDITION">>=><<"BAR'D AGAIN">>},
566 true = {200, ExpectedNewPreferences} == get_preferences_consul(C),
567 true = {200, ExpectedNewPreferences} == get_preferences_cdap(C),
569 %try to give it a smart where there are keys in just config
570 SmartReconfigConfig = {[{<<"foo REDUX EDITION">>, <<"FOO'D AGAIN">>}]},
571 {200, _} = httpabs:put(?XER, ?SC([?PLG(broker_app_url, C), "/reconfigure"]), "application/json", jiffy:encode({[{<<"reconfiguration_type">>, <<"program-flowlet-smart">>},{<<"config">>, SmartReconfigConfig}]})),
572 %make sure CDAP and Consul agree and are equal to what we expected
573 ExpectedNewConfig = #{<<"LEAVE ME ALONE">>=><<"CONFIG EDITION">>,<<"foo REDUX EDITION">>=><<"FOO'D AGAIN">>},
574 true = {200, ExpectedNewConfig} == get_config_consul(C),
575 true = {200, ExpectedNewConfig} == get_config_cdap(C),
577 %try to give it a smart where there are keys in both preferences and config
578 SmartReconfigBoth = {[{<<"foo REDUX EDITION">>, <<"FOO'D AGAIN AGAIN">>}, {<<"preffoo REDUX EDITION">>, <<"BAR'D AGAIN AGAIN">>}]},
579 {200, _} = httpabs:put(?XER, ?SC([?PLG(broker_app_url, C), "/reconfigure"]), "application/json", jiffy:encode({[{<<"reconfiguration_type">>, <<"program-flowlet-smart">>},{<<"config">>, SmartReconfigBoth}]})),
580 ExpectedNewPreferencesBoth = #{<<"LEAVE ME ALONE">>=><<"PREFERENCES EDITION">>,<<"preffoo REDUX EDITION">>=><<"BAR'D AGAIN AGAIN">>},
581 ExpectedNewConfigBoth = #{<<"LEAVE ME ALONE">>=><<"CONFIG EDITION">>,<<"foo REDUX EDITION">>=><<"FOO'D AGAIN AGAIN">>},
582 true = {200, ExpectedNewPreferencesBoth} == get_preferences_consul(C),
583 true = {200, ExpectedNewPreferencesBoth} == get_preferences_cdap(C),
584 true = {200, ExpectedNewConfigBoth} == get_config_consul(C),
585 true = {200, ExpectedNewConfigBoth} == get_config_cdap(C),
587 %try to give it a smart where there are no overlaps
588 SmartReconfigNone = {[{<<"EMPTY">>, <<"LIKE YOUR SOUL">>}]},
589 {400, _} = httpabs:put(?XER, ?SC([?PLG(broker_app_url, C), "/reconfigure"]), "application/json", jiffy:encode({[{<<"reconfiguration_type">>, <<"program-flowlet-smart">>},{<<"config">>, SmartReconfigNone}]})),
590 true = {200, ExpectedNewPreferencesBoth} == get_preferences_consul(C),
591 true = {200, ExpectedNewPreferencesBoth} == get_preferences_cdap(C),
592 true = {200, ExpectedNewConfigBoth} == get_config_consul(C),
593 true = {200, ExpectedNewConfigBoth} == get_config_cdap(C)
596 app_botch_flows(C) ->
598 {200, _} = httpabs:get(?XER,?SC([?PLG(broker_app_url, C), "/healthcheck"])),
600 %purposely shut down a flow "manually" to test that undeploy works with a "partially deployed" app
601 {200, []} = cdap_interface:exec_programs(?XER, ?PLG(appname, C), ?PLG(namespace, C), ?PLG(cdap_url, C),
602 [#program{type = <<"flows">>, id = <<"WhoFlow">>}, #program{type = <<"services">>, id = <<"Greeting">>}], "stop"),
603 %make sure healthcheck now fails
604 {400, _} = httpabs:get(?XER,?SC([?PLG(broker_app_url, C), "/healthcheck"]))
607 app_botch_delete(C) ->
608 %purposely shut down flows and then delete the app from the CDAP api to test undeploy works with a [gone] app
609 {200, []} = cdap_interface:exec_programs(?XER, ?PLG(appname, C), ?PLG(namespace, C), ?PLG(cdap_url, C),
610 [#program{type = <<"flows">>, id = <<"WhoFlow">>}, #program{type = <<"services">>, id = <<"Greeting">>}], "stop"),
611 {200, []} = cdap_interface:delete_app(?XER, ?PLG(appname, C), ?PLG(namespace, C), ?PLG(cdap_url, C)),
613 %make sure healthcheck now fails
614 {400, _} = httpabs:get(?XER,?SC([?PLG(broker_app_url, C), "/healthcheck"]))
617 app_botch_consul_delete(C) ->
618 %purposefully delete the config in consul to make sure delete doesnt blow up
619 {200, "true"} = consul_interface:consul_delete_config(?XER, ?PLG(appname, C),?PLG(consul_url, C)).
622 %Test app teardown and delete
623 %app is there for now in broker
624 {200,_ } = httpabs:get(?XER,?PLG(broker_app_url, C)),
626 %teardown the test application
627 {200, []} = httpabs:delete(?XER, ?PLG(broker_app_url, C)),
629 %make sure the broker deleted the config from Consul
630 {404, _} = get_config_consul(C),
632 %make sure broker deleted the preferences
633 {404, _} = get_preferences_consul(C),
635 %make sure the broker app url no longer exists
636 {404, _ } = httpabs:get(?XER,?PLG(broker_app_url, C)),
638 %teardown the testing rels
639 {404, _} = setup_rels(C, teardown),
641 %teardown the fake service and make sure it is gone
642 {200, Srv} = setup_fake_testing_service(C, teardown),
646 {404,"State: Not Found. Return Body: 'application:testns.hwtest.-SNAPSHOT' was not found."} = httpabs:get(?XER,?PLG(app_url, C)),
648 %make sure that the service is not registered. TODO! Could get more fancy by manually checking a healthcheck
649 {200, RBHC} = httpabs:get(?XER,?PLG(consul_app_url, C)),
650 true = jiffy:decode(RBHC) == [].
652 hydrator_app_teardown(C) ->
653 %Test app teardown and delete
654 %app is there for now in cdap
655 {200, _} = httpabs:get(?XER,?PLG(hydrator_app_url, C)),
657 {200,_ } = httpabs:get(?XER,?PLG(broker_hydrator_app_url, C)),
658 %teardown the test application
659 {200, []} = httpabs:delete(?XER, ?PLG(broker_hydrator_app_url, C)),
660 %make sure the broker deleted the config from Consul
661 ?D(<<"todo! put this back:">>),
662 %{404, _} = get_config_consul(C),
663 %make sure the broker app url no longer exists
664 {404, _ } = httpabs:get(?XER,?PLG(broker_hydrator_app_url, C)),
665 %make sure gone from CDAP
666 {404,"State: Not Found. Return Body: 'application:testns.hydratortest.-SNAPSHOT' was not found."} = httpabs:get(?XER,?PLG(hydrator_app_url, C)),
667 %make sure that the service is not registered. TODO! Could get more fancy by manually checking a healthcheck
668 {200, RBHC} = httpabs:get(?XER,?PLG(consul_hydrator_app_url, C)),
669 true = jiffy:decode(RBHC) == []
672 hydrator_wdeps_test(C) ->
673 %test te app by injecting some data into the stream and getting it out
674 %Sleeping since HTTP services may still be booting up: see https://issues.cask.co/browse/CDAP-812
675 ok = timer:sleep(30000), %30s
678 {200, TestData} = httpabs:get(?XER,?PLG(hydrator_wdeps_test_data_url, C)),
680 {200, _} = httpabs:post(?XER, ?SC([?PLG(hydrator_wdeps_stream_url, C), "/batch"]), "text/plain", TestData),
682 PB = jiffy:encode({[{<<"query">>, <<"select ts from dataset_t1file">>}]}),
683 {200, RB} = httpabs:post(?XER, ?SC([?PLG(cdap_ns_url, C), "/data/explore/queries"]), "text/plain", PB),
684 {[{<<"handle">>, Handle}]} = jiffy:decode(RB),
685 %results can take time, sleep again
686 ok = timer:sleep(30000),
688 {<<"status">>,<<"FINISHED">>},
689 {<<"hasResults">>,true}
691 {200, RB2} = httpabs:get(?XER,?SC([?PLG(cdap_url, C), "/v3/data/explore/queries", "/", Handle, "/status"])),
692 true = Expected == jiffy:decode(RB2),
693 {200, _} = httpabs:post(?XER, ?SC([?PLG(cdap_url, C), "/v3/data/explore/queries", "/", Handle, "/next"]), "text/plain", "")
696 hydrator_wdeps_teardown(C) ->
697 %Test app teardown and delete
698 %app is there for now in cdap
699 {200, _} = httpabs:get(?XER,?PLG(hydrator_wdeps_app_url, C)),
701 {200,_ } = httpabs:get(?XER,?PLG(broker_hydrator_wdeps_app_url, C)),
702 %teardown the test application
703 {200, []} = httpabs:delete(?XER, ?PLG(broker_hydrator_wdeps_app_url, C)),
704 %make sure the broker deleted the config from Consul
705 ?D(<<"todo! put this back:">>),
706 %{404, _} = get_config_consul(C),
707 %make sure the broker app url no longer exists
708 {404, _ } = httpabs:get(?XER,?PLG(broker_hydrator_wdeps_app_url, C)),
709 %make sure gone from CDAP
710 {404,"State: Not Found. Return Body: 'application:testns.hydratortest.-SNAPSHOT' was not found."} = httpabs:get(?XER,?PLG(hydrator_app_url, C)),
711 %make sure that the service is not registered. TODO! Could get more fancy by manually checking a healthcheck
712 {200, RBHC} = httpabs:get(?XER,?PLG(consul_hydrator_wdeps_app_url, C)),
713 true = jiffy:decode(RBHC) == []
717 %test things that should fail
718 %delete a non-existent app
719 {404, "State: Not Found. Return Body: Tried to delete an application that was not registered"} =
720 httpabs:delete(?XER, ?SC([?PLG(broker_app_url, C), "MYFRIENDOFMISERY"])),
722 %malformed Broker put
723 URL = ?SC([?PLG(broker_app_url, C), "FAILURETEST"]),
725 %deploy a bad CDAP app with a bad program_id
727 {<<"cdap_application_type">>, <<"program-flowlet">>},
728 {<<"namespace">>, ?PLG(namespace, C)},
729 {<<"streamname">>, ?PLG(streamname, C)},
730 {<<"jar_url">>, ?PLG(jar_url, C)},
731 {<<"artifact_name">>, ?PLG(art_name, C)},
732 {<<"artifact_version">>, ?PLG(art_ver, C)},
733 {<<"app_config">>, ?PLG(init_config, C)},
734 {<<"app_preferences">>, ?PLG(init_preferences, C)},
735 {<<"services">>, [{[{<<"service_name">>, <<"Greeting">>},
736 {<<"service_endpoint">>, <<"greet">>},
737 {<<"endpoint_method">>, <<"GET">>}]}]},
739 {[{<<"program_type">>, <<"flows">>},
740 {<<"program_id">>, <<"DISSAPOINTMENT">>}]}
742 {<<"program_preferences">>, []}
745 %{404,"State: Not Found. Return Body: State: Not Found. Return Body: 'program:testns.hwtestFAILURETEST.flow.DISSAPOINTMENT' was not found."} = httpabs:put(?XER, URL, "application/json", jiffy:encode(Body2)),
746 %WORKS IN CDAP 4 (looks like they are doing more intrispection on the jar name)
747 {404,_} = httpabs:put(?XER, URL, "application/json", jiffy:encode(Body2)),
748 %make sure the rollback happened
749 {200, "[]"} = httpabs:get(?XER,?SC([?PLG(broker_url, C), "/application"])),
751 %try to deploy with a bad URL where bad means nonexistent (504)
753 {<<"cdap_application_type">>, <<"program-flowlet">>},
754 {<<"namespace">>, ?PLG(namespace, C)},
755 {<<"streamname">>, ?PLG(streamname, C)},
756 {<<"jar_url">>, ?SC([?PLG(jar_url, C), "DOESNOTEXISTMOSTLIKELY"])},
757 {<<"artifact_name">>, ?PLG(art_name, C)},
758 {<<"artifact_version">>, ?PLG(art_ver, C)},
759 {<<"app_config">>, ?PLG(init_config, C)},
760 {<<"app_preferences">>, ?PLG(init_preferences, C)},
761 {<<"services">>, [{[{<<"service_name">>, <<"Greeting">>}, {<<"service_endpoint">>, <<"greet">>}, {<<"endpoint_method">>, <<"GET">>}]}]},
762 {<<"programs">>, [{[{<<"program_type">>, <<"flows">>},{<<"program_id">>, <<"WhoFlow">>}]},{[{<<"program_type">>, <<"services">>},{<<"program_id">>, <<"Greeting">>}]}]},
763 {<<"program_preferences">>, [{[{<<"program_type">>,<<"flows">>}, {<<"program_id">>, <<"WhoFlow">>}, {<<"program_pref">>, ?PLG(whoflowpref, C)}]}]}
765 {404, _} = httpabs:put(?XER, URL, "application/json", jiffy:encode(Body3)).
769 Body1 = jiffy:encode({[{<<"ids">>, [<<"hwtest">>]}]}),
770 {400,"State: Bad Request. Return Body: Invalid PUT Body"} = httpabs:post(?XER, ?SC([?PLG(broker_url, C), "/application/delete"]), "application/json", Body1),
771 %test invalid: not a list
772 Body2 = jiffy:encode({[{<<"appnames">>, <<"hwtest">>}]}),
773 {400,"State: Bad Request. Return Body: Invalid PUT Body"} = httpabs:post(?XER, ?SC([?PLG(broker_url, C), "/application/delete"]), "application/json", Body2),
774 %test undeploy a real app and also an app that is not deployed
775 Body3 = jiffy:encode({[{<<"appnames">>, [<<"hwtest">>, <<"dissapointment">>]}]}),
776 {200, "[200,404]"} = httpabs:post(?XER, ?SC([?PLG(broker_url, C), "/application/delete"]), "application/json", Body3),
777 %teardown the fake service and make sure it is gone
778 {200, Srv} = setup_fake_testing_service(C, teardown),