1 /*
2  * librdkafka - Apache Kafka C library
3  *
4  * Copyright (c) 2020, Magnus Edenhill
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright notice,
11  *    this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright notice,
13  *    this list of conditions and the following disclaimer in the documentation
14  *    and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include "test.h"
30 
31 #include "rdkafka.h"
32 
33 /**
34  * @name Verify KIP-511, client.software.name and client.software.version
35  *
36  */
37 static char jmx_cmd[512];
38 
39 /**
40  * @brief Verify that the expected software name and version is reported
41  *        in JMX metrics.
42  */
43 static void jmx_verify (const char *exp_swname, const char *exp_swversion) {
44 #if _WIN32
45         return;
46 #else
47         int r;
48         char cmd[512+256];
49 
50         if (!*jmx_cmd)
51                 return;
52 
53         rd_snprintf(cmd, sizeof(cmd),
54                     "%s | "
55                     "grep -F 'clientSoftwareName=%s,clientSoftwareVersion=%s'",
56                     jmx_cmd,
57                     exp_swname, exp_swversion ? exp_swversion : "");
58         r = system(cmd);
59         if (WEXITSTATUS(r) == 1)
60                 TEST_FAIL("Expected software name and version not found in "
61                           "JMX metrics with command \"%s\"", cmd);
62         else if (r == -1 || WIFSIGNALED(r) || WEXITSTATUS(r))
63                 TEST_FAIL("Failed to execute JmxTool command \"%s\": "
64                           "exit code %d", cmd, r);
65 
66         TEST_SAY("Expected software name \"%s\" and version \"%s\" "
67                  "found in JMX metrics\n",
68                  exp_swname, exp_swversion);
69 #endif /* !_WIN32 */
70 }
71 
72 
73 static void do_test_swname (const char *broker,
74                             const char *swname, const char *swversion,
75                             const char *exp_swname, const char *exp_swversion) {
76         rd_kafka_t *rk;
77         rd_kafka_conf_t *conf;
78         const rd_kafka_metadata_t *md;
79         rd_kafka_resp_err_t err;
80 
81         TEST_SAY(_C_MAG "[ Test client.software.name=%s, "
82                  "client.software.version=%s ]\n",
83                  swname ? swname : "NULL", swversion ? swversion : "NULL");
84 
85         test_conf_init(&conf, NULL, 30 /* jmxtool is severely slow */);
86         if (broker)
87                 test_conf_set(conf, "bootstrap.servers", broker);
88         if (swname)
89                 test_conf_set(conf, "client.software.name", swname);
90         if (swversion)
91                 test_conf_set(conf, "client.software.version", swversion);
92         rk = test_create_handle(RD_KAFKA_PRODUCER, conf);
93 
94         /* Trigger a metadata request so we know we're connected. */
95         err = rd_kafka_metadata(rk, 0, NULL, &md, tmout_multip(5000));
96         TEST_ASSERT(!err, "metadata() failed: %s", rd_kafka_err2str(err));
97         rd_kafka_metadata_destroy(md);
98 
99         /* Verify JMX metrics, if possible */
100         jmx_verify(exp_swname, exp_swversion);
101 
102         rd_kafka_destroy(rk);
103 
104         TEST_SAY(_C_GRN "[ Test client.software.name=%s, "
105                  "client.software.version=%s: PASS ]\n",
106                  swname ? swname : "NULL", swversion ? swversion : "NULL");
107 }
108 
109 int main_0016_client_swname (int argc, char **argv) {
110         const char *broker;
111         const char *kafka_path;
112         const char *jmx_port;
113         const char *reason = NULL;
114 
115         /* If available, use the Kafka JmxTool to query software name
116          * in broker JMX metrics */
117         if (!(broker = test_getenv("BROKER_ADDRESS_2", NULL)))
118                 reason = "Env var BROKER_ADDRESS_2 missing "
119                         "(not running in trivup or trivup too old?)";
120         else if (test_broker_version < TEST_BRKVER(2,5,0,0))
121                 reason = "Client software JMX metrics not exposed prior to "
122                         "Apache Kafka 2.5.0.0";
123         else if (!(kafka_path = test_getenv("KAFKA_PATH", NULL)))
124                 reason = "Env var KAFKA_PATH missing (not running in trivup?)";
125         else if (!(jmx_port = test_getenv("BROKER_JMX_PORT_2", NULL)))
126                 reason = "Env var BROKER_JMX_PORT_2 missing "
127                         "(not running in trivup or trivup too old?)";
128         else
129                 rd_snprintf(jmx_cmd, sizeof(jmx_cmd),
130                             "%s/bin/kafka-run-class.sh kafka.tools.JmxTool "
131                             "--jmx-url "
132                             "service:jmx:rmi:///jndi/rmi://:%s/jmxrmi "
133                             "--attributes connections --one-time true | "
134                             "grep clientSoftware",
135                             kafka_path, jmx_port);
136 
137         if (reason)
138                 TEST_WARN("Will not be able to verify JMX metrics: %s\n",
139                           reason);
140 
141         /* Default values, the version is not checked since the
142          * built librdkafka may not use the same string, and additionally we
143          * don't want to perform the string mangling here to make the string
144          * protocol safe. */
145         do_test_swname(broker, NULL, NULL, "librdkafka", NULL);
146         /* Properly formatted */
147         do_test_swname(broker,
148                        "my-little-version", "1.2.3.4",
149                        "my-little-version", "1.2.3.4");
150         /* Containing invalid characters, verify that safing the strings works */
151         do_test_swname(broker,
152                        "?1?this needs! ESCAPING?", "--v99.11 ~b~",
153                        "1-this-needs--ESCAPING", "v99.11--b");
154 
155         return 0;
156 }
157 
158