HostsExportWorkflowTests.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. using Tests.EndToEnd.Infra;
  2. using Xunit.Abstractions;
  3. namespace Tests.EndToEnd.ExporterTests;
  4. [Collection("Yaml CLI tests")]
  5. public class HostsExportWorkflowTests(
  6. TempYamlCliFixture fs,
  7. ITestOutputHelper outputHelper)
  8. : IClassFixture<TempYamlCliFixture> {
  9. private async Task<(string output, string yaml)> ExecuteAsync(params string[] args) {
  10. outputHelper.WriteLine($"rpk {string.Join(" ", args)}");
  11. var output = await YamlCliTestHost.RunAsync(
  12. args,
  13. fs.Root,
  14. outputHelper,
  15. "config.yaml");
  16. outputHelper.WriteLine(output);
  17. var yaml = await File.ReadAllTextAsync(Path.Combine(fs.Root, "config.yaml"));
  18. return (output, yaml);
  19. }
  20. [Fact]
  21. public async Task hosts_export_basic_workflow_test() {
  22. await File.WriteAllTextAsync(Path.Combine(fs.Root, "config.yaml"), """
  23. version: 1
  24. resources:
  25. - kind: System
  26. type: vm
  27. name: vm-web01
  28. labels:
  29. ip: 192.168.1.10
  30. - kind: System
  31. type: vm
  32. name: vm-db01
  33. labels:
  34. ip: 192.168.1.20
  35. """);
  36. (var output, var _) = await ExecuteAsync(
  37. "hosts", "export",
  38. "--no-header",
  39. "--no-localhost"
  40. );
  41. Assert.Equal("""
  42. Generated Hosts File:
  43. 192.168.1.20 vm-db01
  44. 192.168.1.10 vm-web01
  45. """, output);
  46. }
  47. [Fact]
  48. public async Task hosts_export_with_domain_suffix_test() {
  49. await File.WriteAllTextAsync(Path.Combine(fs.Root, "config.yaml"), """
  50. version: 1
  51. resources:
  52. - kind: System
  53. type: vm
  54. name: vm1
  55. labels:
  56. ip: 10.0.0.1
  57. """);
  58. (var output, var _) = await ExecuteAsync(
  59. "hosts", "export",
  60. "--domain-suffix", "home.local",
  61. "--no-header",
  62. "--no-localhost"
  63. );
  64. Assert.Equal("""
  65. Generated Hosts File:
  66. 10.0.0.1 vm1.home.local
  67. """, output);
  68. }
  69. [Fact]
  70. public async Task hosts_export_respects_tag_filter() {
  71. await File.WriteAllTextAsync(Path.Combine(fs.Root, "config.yaml"), """
  72. version: 1
  73. resources:
  74. - kind: System
  75. type: vm
  76. name: prod-vm
  77. tags:
  78. - prod
  79. labels:
  80. ip: 10.0.0.1
  81. - kind: System
  82. type: vm
  83. name: staging-vm
  84. tags:
  85. - staging
  86. labels:
  87. ip: 10.0.0.2
  88. """);
  89. (var output, var _) = await ExecuteAsync(
  90. "hosts", "export",
  91. "--include-tags", "prod",
  92. "--no-header",
  93. "--no-localhost"
  94. );
  95. Assert.Contains("10.0.0.1 prod-vm", output);
  96. Assert.DoesNotContain("staging-vm", output);
  97. }
  98. [Fact]
  99. public async Task hosts_export_is_sorted_by_name() {
  100. await File.WriteAllTextAsync(Path.Combine(fs.Root, "config.yaml"), """
  101. version: 1
  102. resources:
  103. - kind: System
  104. type: vm
  105. name: b-host
  106. labels:
  107. ip: 10.0.0.2
  108. - kind: System
  109. type: vm
  110. name: a-host
  111. labels:
  112. ip: 10.0.0.1
  113. """);
  114. (var output, var _) = await ExecuteAsync(
  115. "hosts", "export",
  116. "--no-header",
  117. "--no-localhost"
  118. );
  119. var aIndex = output.IndexOf("a-host", StringComparison.Ordinal);
  120. var bIndex = output.IndexOf("b-host", StringComparison.Ordinal);
  121. Assert.True(aIndex < bIndex);
  122. }
  123. [Fact]
  124. public async Task hosts_export_uses_hostname_label_when_no_ip() {
  125. // hosts-file-export.md §1: the `hostname` label is an alternative
  126. // address. A resource with only `hostname` should still appear, with
  127. // the hostname taking the address column.
  128. await File.WriteAllTextAsync(Path.Combine(fs.Root, "config.yaml"), """
  129. version: 1
  130. resources:
  131. - kind: System
  132. type: vm
  133. name: vm-hostname
  134. labels:
  135. hostname: vm-hostname.lan
  136. """);
  137. (var output, var _) = await ExecuteAsync(
  138. "hosts", "export",
  139. "--no-header",
  140. "--no-localhost"
  141. );
  142. Assert.Contains("vm-hostname.lan vm-hostname", output);
  143. }
  144. [Fact]
  145. public async Task hosts_export_uses_ansible_host_label_when_no_ip_or_hostname() {
  146. // hosts-file-export.md §1: "If you already use Ansible, `ansible_host`
  147. // also works." A resource with only ansible_host should appear in
  148. // the hosts file.
  149. await File.WriteAllTextAsync(Path.Combine(fs.Root, "config.yaml"), """
  150. version: 1
  151. resources:
  152. - kind: System
  153. type: vm
  154. name: vm-ansible
  155. labels:
  156. ansible_host: 10.0.0.99
  157. """);
  158. (var output, var _) = await ExecuteAsync(
  159. "hosts", "export",
  160. "--no-header",
  161. "--no-localhost"
  162. );
  163. Assert.Contains("10.0.0.99 vm-ansible", output);
  164. }
  165. [Fact]
  166. public async Task hosts_export_prefers_ip_over_hostname_and_ansible_host() {
  167. // hosts-file-export.md implies a precedence: ip is the canonical
  168. // address, with hostname/ansible_host only as fallbacks. When all
  169. // three are present the `ip` value wins.
  170. await File.WriteAllTextAsync(Path.Combine(fs.Root, "config.yaml"), """
  171. version: 1
  172. resources:
  173. - kind: System
  174. type: vm
  175. name: vm-all
  176. labels:
  177. ip: 10.0.0.1
  178. hostname: not-this.lan
  179. ansible_host: 10.0.0.2
  180. """);
  181. (var output, var _) = await ExecuteAsync(
  182. "hosts", "export",
  183. "--no-header",
  184. "--no-localhost"
  185. );
  186. Assert.Contains("10.0.0.1 vm-all", output);
  187. Assert.DoesNotContain("not-this.lan", output);
  188. Assert.DoesNotContain("10.0.0.2", output);
  189. }
  190. [Fact]
  191. public async Task hosts_export_skips_resources_without_address() {
  192. await File.WriteAllTextAsync(Path.Combine(fs.Root, "config.yaml"), """
  193. version: 1
  194. resources:
  195. - kind: System
  196. type: vm
  197. name: with-ip
  198. labels:
  199. ip: 10.0.0.1
  200. - kind: System
  201. type: vm
  202. name: without-ip
  203. """);
  204. (var output, var _) = await ExecuteAsync(
  205. "hosts", "export",
  206. "--no-header",
  207. "--no-localhost"
  208. );
  209. Assert.Contains("10.0.0.1 with-ip", output);
  210. Assert.DoesNotContain("without-ip", output);
  211. }
  212. }