| 1 | /*++ |
| 2 | |
| 3 | Copyright (c) Microsoft. All rights reserved. |
| 4 | |
| 5 | Module Name: |
| 6 | |
| 7 | ContainerModel.h |
| 8 | |
| 9 | Abstract: |
| 10 | |
| 11 | This file contains the ContainerModel definitions |
| 12 | |
| 13 | --*/ |
| 14 | |
| 15 | #pragma once |
| 16 | |
| 17 | #include "MountSpecParsing.h" |
| 18 | #include <wslservice.h> |
| 19 | #include <wslc.h> |
| 20 | #include <optional> |
| 21 | #include <string> |
| 22 | #include <vector> |
| 23 | |
| 24 | namespace wsl::windows::wslc::models { |
| 25 | |
| 26 | namespace mount = wsl::windows::common::mount; |
| 27 | |
| 28 | // Valid formats for container list output. |
| 29 | enum class FormatType |
| 30 | { |
| 31 | Table, |
| 32 | Json, |
| 33 | }; |
| 34 | |
| 35 | struct ContainerNetwork |
| 36 | { |
| 37 | std::string Name; |
| 38 | std::vector<std::string> Aliases; |
| 39 | }; |
| 40 | |
| 41 | enum class PullPolicy |
| 42 | { |
| 43 | Missing, |
| 44 | Always, |
| 45 | Never, |
| 46 | }; |
| 47 | |
| 48 | // Progress output style for `wslc build`. Auto resolves to Tty when progress output is an |
| 49 | // interactive VT console and Plain otherwise. |
| 50 | enum class ProgressMode |
| 51 | { |
| 52 | Auto, |
| 53 | Tty, |
| 54 | Plain, |
| 55 | Quiet, |
| 56 | }; |
| 57 | |
| 58 | struct ContainerOptions |
| 59 | { |
| 60 | std::vector<std::string> Arguments; |
| 61 | std::vector<std::string> EnvironmentVariables; |
| 62 | bool Detach = false; |
| 63 | bool Interactive = false; |
| 64 | std::string Name; |
| 65 | bool Remove = false; |
| 66 | bool TTY = false; |
| 67 | bool PublishAll = false; |
| 68 | WSLCSignal StopSignal = WSLCSignalNone; |
| 69 | std::optional<int> StopTimeout{}; |
| 70 | std::optional<int64_t> ShmSize{}; |
| 71 | std::optional<std::string> HealthCmd{}; |
| 72 | std::optional<int64_t> HealthInterval{}; // nanoseconds |
| 73 | std::optional<int64_t> HealthTimeout{}; // nanoseconds |
| 74 | std::optional<int64_t> HealthStartPeriod{}; // nanoseconds |
| 75 | std::optional<int> HealthRetries{}; |
| 76 | bool NoHealthcheck = false; |
| 77 | bool Gpu = false; |
| 78 | std::vector<std::string> Ports; |
| 79 | std::vector<mount::Spec> Mounts; |
| 80 | std::string WorkingDirectory; |
| 81 | std::vector<std::string> Entrypoint; |
| 82 | std::optional<std::string> User{}; |
| 83 | std::optional<std::string> Hostname{}; |
| 84 | std::optional<std::string> Domainname{}; |
| 85 | std::vector<std::string> DnsServers; |
| 86 | std::vector<std::string> DnsSearchDomains; |
| 87 | std::vector<std::string> DnsOptions; |
| 88 | std::vector<ContainerNetwork> Networks; |
| 89 | std::vector<std::string> NetworkAliases; |
| 90 | std::optional<std::string> IpAddress{}; |
| 91 | std::vector<std::string> Tmpfs; |
| 92 | std::vector<std::pair<std::string, std::string>> Labels; |
| 93 | std::optional<std::wstring> CidFile{}; |
| 94 | std::optional<int64_t> MemoryBytes{}; |
| 95 | std::optional<int64_t> NanoCpus{}; |
| 96 | std::vector<std::tuple<std::string, int64_t, int64_t>> Ulimits; |
| 97 | PullPolicy Pull = PullPolicy::Missing; |
| 98 | }; |
| 99 | |
| 100 | struct CreateContainerResult |
| 101 | { |
| 102 | std::string Id; |
| 103 | }; |
| 104 | |
| 105 | struct StopContainerOptions |
| 106 | { |
| 107 | WSLCSignal Signal = WSLCSignalNone; |
| 108 | LONG Timeout = WSLC_STOP_TIMEOUT_DEFAULT; |
| 109 | }; |
| 110 | |
| 111 | struct PruneContainersResult |
| 112 | { |
| 113 | std::vector<std::string> PrunedContainers; |
| 114 | ULONGLONG SpaceReclaimed{}; |
| 115 | }; |
| 116 | |
| 117 | struct KillContainerOptions |
| 118 | { |
| 119 | int Signal = WSLCSignalSIGKILL; |
| 120 | }; |
| 121 | |
| 122 | struct PortInformation |
| 123 | { |
| 124 | uint16_t HostPort{}; |
| 125 | uint16_t ContainerPort{}; |
| 126 | int Protocol{}; // IP protocol number (e.g., IPPROTO_TCP or IPPROTO_UDP) |
| 127 | std::string BindingAddress; |
| 128 | |
| 129 | NLOHMANN_DEFINE_TYPE_INTRUSIVE(PortInformation, HostPort, ContainerPort, Protocol, BindingAddress); |
| 130 | }; |
| 131 | |
| 132 | struct ContainerInformation |
| 133 | { |
| 134 | std::string Id; |
| 135 | std::string Name; |
| 136 | std::string Image; |
| 137 | // Command and runtime supplied status description. |
| 138 | std::string Command; |
| 139 | std::string Status; |
| 140 | std::string Labels; |
| 141 | std::string Networks; |
| 142 | std::string Mounts; |
| 143 | ULONG LocalVolumes{}; |
| 144 | WSLCContainerState State; |
| 145 | LONGLONG StateChangedAt{}; |
| 146 | LONGLONG CreatedAt{}; |
| 147 | std::vector<PortInformation> Ports; |
| 148 | }; |
| 149 | |
| 150 | // The platform a container runs on. Emitted as a nested object to match docker. |
| 151 | struct ContainerPlatform |
| 152 | { |
| 153 | std::string architecture; |
| 154 | std::string os; |
| 155 | |
| 156 | NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(ContainerPlatform, architecture, os); |
| 157 | }; |
| 158 | |
| 159 | // The shape emitted by "container list --format json". |
| 160 | struct ContainerOutputInformation |
| 161 | { |
| 162 | std::string Command; |
| 163 | std::string CreatedAt; |
| 164 | std::string HealthStatus; |
| 165 | std::string ID; |
| 166 | std::string Image; |
| 167 | std::string Labels; |
| 168 | std::string LocalVolumes; |
| 169 | std::string Mounts; |
| 170 | std::string Names; |
| 171 | std::string Networks; |
| 172 | ContainerPlatform Platform; |
| 173 | std::string Ports; |
| 174 | std::string RunningFor; |
| 175 | std::string Size; |
| 176 | std::string State; |
| 177 | std::string Status; |
| 178 | |
| 179 | NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT( |
| 180 | ContainerOutputInformation, Command, CreatedAt, HealthStatus, ID, Image, Labels, LocalVolumes, Mounts, Names, Networks, Platform, Ports, RunningFor, Size, State, Status); |
| 181 | }; |
| 182 | |
| 183 | struct EnvironmentVariable |
| 184 | { |
| 185 | static std::optional<std::wstring> Parse(const std::wstring& entry); |
| 186 | static std::vector<std::wstring> ParseFile(const std::wstring& filePath); |
| 187 | }; |
| 188 | |
| 189 | struct PublishPort |
| 190 | { |
| 191 | enum class Protocol |
| 192 | { |
| 193 | UDP, |
| 194 | TCP, |
| 195 | }; |
| 196 | |
| 197 | struct PortRange |
| 198 | { |
| 199 | PortRange(uint16_t start, uint16_t end) : m_start(start), m_end(end) |
| 200 | { |
| 201 | } |
| 202 | |
| 203 | uint16_t Start() const |
| 204 | { |
| 205 | return m_start; |
| 206 | } |
| 207 | |
| 208 | uint16_t End() const |
| 209 | { |
| 210 | return m_end; |
| 211 | } |
| 212 | |
| 213 | constexpr uint16_t Count() const noexcept |
| 214 | { |
| 215 | return (m_end >= m_start) ? (m_end - m_start + 1) : 0; |
| 216 | } |
| 217 | |
| 218 | constexpr bool IsSingle() const noexcept |
| 219 | { |
| 220 | return Count() == 1; |
| 221 | } |
| 222 | |
| 223 | constexpr bool IsValid() const noexcept |
| 224 | { |
| 225 | return Count() > 0 && IsValidPort(m_start) && IsValidPort(m_end); |
| 226 | } |
| 227 | |
| 228 | constexpr bool IsEphemeral() const noexcept |
| 229 | { |
| 230 | return m_start == WSLC_EPHEMERAL_PORT && m_end == WSLC_EPHEMERAL_PORT; |
| 231 | } |
| 232 | |
| 233 | static PublishPort::PortRange ParsePortPart(const std::string& portPart); |
| 234 | static PublishPort::PortRange Ephemeral() noexcept |
| 235 | { |
| 236 | return {WSLC_EPHEMERAL_PORT, WSLC_EPHEMERAL_PORT}; |
| 237 | } |
| 238 | |
| 239 | private: |
| 240 | uint16_t m_start{}; |
| 241 | uint16_t m_end{}; |
| 242 | }; |
| 243 | |
| 244 | struct IPAddress |
| 245 | { |
| 246 | explicit IPAddress(std::string ip) : m_ip(std::move(ip)) |
| 247 | { |
| 248 | if (!m_ip.empty() && m_ip.front() == '[' && m_ip.back() == ']') |
| 249 | { |
| 250 | m_isIPv6 = true; |
| 251 | m_ip = m_ip.substr(1, m_ip.size() - 2); |
| 252 | } |
| 253 | } |
| 254 | |
| 255 | std::string IP() const |
| 256 | { |
| 257 | return m_ip; |
| 258 | } |
| 259 | |
| 260 | bool IsIPv6() const |
| 261 | { |
| 262 | return m_isIPv6; |
| 263 | } |
| 264 | |
| 265 | private: |
| 266 | bool m_isIPv6 = false; |
| 267 | std::string m_ip{}; |
| 268 | }; |
| 269 | |
| 270 | static constexpr uint16_t MAX_PORT = std::numeric_limits<uint16_t>::max(); |
| 271 | static constexpr uint16_t MIN_PORT = 1; |
| 272 | |
| 273 | std::optional<IPAddress> HostIP() const noexcept |
| 274 | { |
| 275 | return m_hostIP; |
| 276 | } |
| 277 | |
| 278 | PortRange HostPort() const noexcept |
| 279 | { |
| 280 | return m_hostPort; |
| 281 | } |
| 282 | |
| 283 | PortRange ContainerPort() const noexcept |
| 284 | { |
| 285 | return m_containerPort; |
| 286 | } |
| 287 | |
| 288 | Protocol PortProtocol() const noexcept |
| 289 | { |
| 290 | return m_protocol; |
| 291 | } |
| 292 | |
| 293 | std::string Original() const noexcept |
| 294 | { |
| 295 | return m_original; |
| 296 | } |
| 297 | |
| 298 | bool IsRangeMapping() const noexcept |
| 299 | { |
| 300 | return !m_containerPort.IsSingle(); |
| 301 | } |
| 302 | |
| 303 | static PublishPort Parse(const std::string& value); |
| 304 | |
| 305 | private: |
| 306 | std::optional<IPAddress> m_hostIP; |
| 307 | PortRange m_hostPort = PortRange::Ephemeral(); |
| 308 | PortRange m_containerPort = PortRange::Ephemeral(); |
| 309 | Protocol m_protocol = Protocol::TCP; |
| 310 | std::string m_original; |
| 311 | void Validate() const; |
| 312 | PublishPort() = default; |
| 313 | static constexpr bool IsValidPort(unsigned long port) noexcept |
| 314 | { |
| 315 | return port >= MIN_PORT && port <= MAX_PORT; |
| 316 | } |
| 317 | }; |
| 318 | |
| 319 | struct VolumeMount |
| 320 | { |
| 321 | std::wstring Host() const |
| 322 | { |
| 323 | return m_host; |
| 324 | } |
| 325 | |
| 326 | std::string ContainerPath() const |
| 327 | { |
| 328 | return m_containerPath; |
| 329 | } |
| 330 | |
| 331 | bool IsReadOnly() const |
| 332 | { |
| 333 | return m_isReadOnlyMode; |
| 334 | } |
| 335 | |
| 336 | bool IsNamedVolume() const |
| 337 | { |
| 338 | return m_isNamedVolume; |
| 339 | } |
| 340 | |
| 341 | static bool IsValidNamedVolumeName(const std::wstring& name); |
| 342 | |
| 343 | static VolumeMount Parse(const std::wstring& value); |
| 344 | |
| 345 | private: |
| 346 | std::wstring m_host; |
| 347 | std::string m_containerPath; |
| 348 | bool m_isReadOnlyMode = false; |
| 349 | bool m_isNamedVolume = false; |
| 350 | }; |
| 351 | |
| 352 | class CidFile |
| 353 | { |
| 354 | public: |
| 355 | explicit CidFile(const std::optional<std::wstring>& path); |
| 356 | ~CidFile(); |
| 357 | |
| 358 | NON_COPYABLE(CidFile); |
| 359 | NON_MOVABLE(CidFile); |
| 360 | |
| 361 | void Commit(const std::string& containerId); |
| 362 | |
| 363 | private: |
| 364 | std::optional<std::wstring> m_path{}; |
| 365 | wil::unique_hfile m_file; |
| 366 | bool m_committed = false; |
| 367 | }; |
| 368 | } // namespace wsl::windows::wslc::models |