Skip to content

Commit

Permalink
Ok git...
Browse files Browse the repository at this point in the history
  • Loading branch information
m-barthelemy committed Jul 4, 2022
1 parent 77fc021 commit 35f6bf0
Show file tree
Hide file tree
Showing 151 changed files with 8,934 additions and 0 deletions.
69 changes: 69 additions & 0 deletions Sources/DockerSwift/APIs/DockerClient+Config.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import Foundation

extension DockerClient {

/// APIs related to Docker configs.
public var configs: ConfigsAPI {
.init(client: self)
}

public struct ConfigsAPI {
fileprivate var client: DockerClient

/// Lists the configs.
/// - Throws: Errors that can occur when executing the request.
/// - Returns: Returns a list of `Config`.
public func list() async throws -> [Config] {
return try await client.run(ListConfigsEndpoint())
}


/// Gets a Config by a given name or id.
/// - Parameter nameOrId: Name or id of a `Config` that should be fetched.
/// - Throws: Errors that can occur when executing the request.
/// - Returns: Return the `Config`.
public func get(_ nameOrId: String) async throws -> Config {
return try await client.run(InspectConfigEndpoint(nameOrId: nameOrId))
}

/// Creates a new Config.
/// - Parameters:
/// - spec: configuration as a `ConfigSpec`.
/// - Throws: Errors that can occur when executing the request.
/// - Returns: Returns the newly created `Config`.
public func create(spec: ConfigSpec) async throws -> Config {
let createResponse = try await client.run(CreateConfigEndpoint(spec: spec))
return try await client.configs.get(createResponse.ID)
}

/// Updates a Config. Currently, only the `labels` field can be updated (Docker limitation)
/// - Parameters:
/// - nameOrId: Name or ID of the `Config` that should be updated.
/// - labels: new labels to set.
/// - Throws: Errors that can occur when executing the request.
public func update(_ nameOrId: String, labels: [String:String]) async throws {
let config = try await get(nameOrId)
try await client.run(
UpdateConfigEndpoint(
nameOrId: config.id,
version: config.version.index,
spec: .init(
name: config.spec.name,
data: config.spec.data,
labels: labels,
templating: config.spec.templating
)
)
)
}

/// Removes an existing Config.
/// - Parameters:
/// - nameOrId: Name or Id of the`config`.
/// - Throws: Errors that can occur when executing the request.
public func remove(_ nameOrId: String) async throws {
try await client.run(RemoveConfigEndpoint(nameOrId: nameOrId))
}

}
}
219 changes: 219 additions & 0 deletions Sources/DockerSwift/APIs/DockerClient+Container.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
import Foundation
import NIO
import AsyncHTTPClient

extension DockerClient {

/// APIs related to containers.
public var containers: ContainersAPI {
.init(client: self)
}

public struct ContainersAPI {
fileprivate var client: DockerClient

/// Fetches all containers in the Docker system.
/// - Parameter all: If `true` all containers are fetched, otherwise only running containers.
/// - Throws: Errors that can occur when executing the request.
/// - Returns: Returns a list of `Container`.
public func list(all: Bool = false) async throws -> [ContainerSummary] {
return try await client.run(ListContainersEndpoint(all: all))
}

/// Fetches the latest information about a container by a given name or id..
/// - Parameter nameOrId: Name or id of a container.
/// - Throws: Errors that can occur when executing the request.
/// - Returns: Returns the `Container` and its information.
public func get(_ nameOrId: String) async throws -> Container {
return try await client.run(InspectContainerEndpoint(nameOrId: nameOrId))
}

/// Creates a new container from a given image. If specified the commands override the default commands from the image.
/// - Parameters:
/// - image: Instance of an `Image`.
/// - commands: Override the default commands from the image. Default `nil`.
/// - Throws: Errors that can occur when executing the request.
/// - Returns: Returns the created `Container`.
public func create(image: Image, commands: [String]? = nil) async throws -> Container {
let response = try await client.run(SimpleCreateContainerEndpoint(imageName: image.id, commands: commands))
let container = try await get(response.Id)
return container
}

/// Creates a new container from a fully customizable config.
/// - Parameters:
/// - name: Custom name for this container. If not set, a random one will be generated by Docker. Must match `/?[a-zA-Z0-9][a-zA-Z0-9_.-]+` (must start with a letter or number, then must only contain letters, numbers, _, ., -)
/// - spec: a `ContainerCreate` representing the container configuration.
/// - Returns: Returns the created `Container`.
public func create(name: String? = nil, spec: ContainerSpec) async throws -> Container {
let response = try await client.run(CreateContainerEndpoint(name: name, spec: spec))
let container = try await get(response.Id)
return container
}

/// Updates an existing container.
/// - Parameters:
/// - nameOrId: Name or id of a container.
/// - spec: a `ContainerUpdate` representing the configuration to update.
/// - Throws: Errors that can occur when executing the request.
public func update(_ nameOrId: String, spec: ContainerUpdate) async throws {
try await client.run(UpdateContainerEndpoint(nameOrId: nameOrId, spec: spec))
}

/// Starts a container. Before starting it needs to be created.
/// - Parameter nameOrId: Name or Id of the`Container`.
/// - Throws: Errors that can occur when executing the request.
public func start(_ nameOrId: String) async throws {
try await client.run(StartContainerEndpoint(containerId: nameOrId))
}

/// Stops a container. Before stopping it needs to be created and started.
/// - Parameter nameOrId: Name or Id of the`Container`.
/// - Throws: Errors that can occur when executing the request.
public func stop(_ nameOrId: String) async throws {
try await client.run(StopContainerEndpoint(containerId: nameOrId))
}

/// Pauses a container.
/// Uses the freezer cgroup to suspend all processes in a container.
/// Traditionally, when suspending a process the SIGSTOP signal is used, which is observable by the process being suspended.
/// With the freezer cgroup the process is unaware, and unable to capture, that it is being suspended, and subsequently resumed.
/// - Parameter nameOrId: Name or Id of the`Container`.
/// - Throws: Errors that can occur when executing the request.
public func pause(_ nameOrId: String) async throws {
try await client.run(PauseUnpauseContainerEndpoint(nameOrId: nameOrId, unpause: false))
}

/// Resume a container which has been paused.
/// - Parameter nameOrId: Name or Id of the`Container`.
/// - Throws: Errors that can occur when executing the request.
public func unpause(_ nameOrId: String) async throws {
try await client.run(PauseUnpauseContainerEndpoint(nameOrId: nameOrId, unpause: true))
}

/// Renames a container.
/// - Parameters:
/// - nameOrId: Name or Id of the`Container`.
/// - to: The new name of the `Container`.
/// - Throws: Errors that can occur when executing the request.
public func rename(_ nameOrId: String, to newName: String) async throws {
try await client.run(RenameContainerEndpoint(containerId: nameOrId, newName: newName))
}

/// Removes an existing container.
/// - Parameters:
/// - nameOrId: Name or Id of the`Container`.
/// - force: Delete even if it is running
/// - removeAnonymousVolumes: Remove anonymous volumes associated with the container.
/// - Throws: Errors that can occur when executing the request.
public func remove(_ nameOrId: String, force: Bool = false, removeAnonymousVolumes: Bool = false) async throws {
try await client.run(RemoveContainerEndpoint(containerId: nameOrId, force: force, removeAnonymousVolumes: removeAnonymousVolumes))
}

/// Gets the logs of a container.
/// - Parameters:
/// - container: Instance of an `Container`.
/// - stdErr: Whether to return log lines from the standard error.
/// - stdOut: Whether to return log lines from the standard output.
/// - timestamps: Whether to return the timestamp of each log line
/// - follow: Whether to wait for new logs to become available and stream them.
/// - tail: Number of last existing log lines to return. Default: all.
/// - Throws: Errors that can occur when executing the request.
/// - Returns: Returns a sequence of `DockerLogEntry`.
public func logs(container: Container, stdErr: Bool = true, stdOut: Bool = true, timestamps: Bool = true, follow: Bool = false, tail: UInt? = nil, since: Date = .distantPast, until: Date = .distantFuture) async throws -> AsyncThrowingStream<DockerLogEntry, Error> {
let endpoint = GetContainerLogsEndpoint(
containerId: container.id,
stdout: stdOut,
stderr: stdErr,
timestamps: timestamps,
follow: follow,
tail: tail == nil ? "all" : "\(tail!)",
since: since,
until: until
)
let response = try await client.run(
endpoint,
// Arbitrary timeouts.
// TODO: should probably make these configurable
timeout: follow ? .hours(12) : .seconds(60),
hasLengthHeader: !container.config.tty,
separators: [UInt8(13)]
)

return try await endpoint.map(response: response, tty: container.config.tty)
}

/// Attaches to a container. Allows to retrieve a stream of the container output, and sending commands if it listens on the standard input.
/// - Parameters:
/// - container: Instance of an `Container`.
/// - stream: Whether to return stream
/// - logs: Whether to return log lines from the standard output.
/// - detachKeys: Override the key sequence for detaching a container. Format is a single character `[a-Z]`, or` ctrl-<value>` where `<value>` is one of: a-z, @, ^, [, ,, or _.
/// - Throws: Errors that can occur when executing the request.
/// - Returns: Returns a `ContainerAttach` allowing to fetch the container output as well as sending input/commands to it.
public func attach(container: Container, stream: Bool, logs: Bool, detachKeys: String? = nil) async throws -> ContainerAttach {
let ep = ContainerAttachEndpoint(client: client, nameOrId: container.id, stream: true, logs: false)
return try await ep.connect()
}

/// Deletes all stopped containers.
/// - Throws: Errors that can occur when executing the request.
/// - Returns: Returns an `EventLoopFuture` with a list of deleted `Container` and the reclaimed space.
public func prune() async throws -> PrunedContainers {
let response = try await client.run(PruneContainersEndpoint())
return PrunedContainers(
containersIds: response.ContainersDeleted?.map({ .init($0)}) ?? [],
reclaimedSpace: response.SpaceReclaimed
)
}

public struct PrunedContainers {
/// IDs of the containers that were deleted.
let containersIds: [String]

/// Disk space reclaimed in bytes.
let reclaimedSpace: UInt64
}

/// Blocks until a container stops, then returns the exit code.
/// - Parameter nameOrId: Name or ID of the`Container`.
/// - Throws: Errors that can occur when executing the request.
/// - Returns: Returns the exit code of the`Container` (`0` meaning success/no error).
public func wait(_ nameOrId: String) async throws -> Int {
let response = try await client.run(WaitContainerEndpoint(nameOrId: nameOrId))
return response.StatusCode
}

/// Returns which files in a container's filesystem have been added, deleted, or modified.
/// - Parameter nameOrId: Name or ID of the`Container`.
/// - Throws: Errors that can occur when executing the request.
/// - Returns: Returns a list of `ContainerFsChange`.
public func getFsChanges(_ nameOrId: String) async throws -> [ContainerFsChange] {
return try await client.run(GetContainerChangesEndpoint(nameOrId: nameOrId))
}

/// Returns `ps`-like raw info about processes running in a container
/// - Parameters:
/// - nameOrId: Name or ID of the`Container`.
/// - psArgs: options to pass to the `ps` command. Defaults to `-ef`
/// - Throws: Errors that can occur when executing the request.
/// - Returns: Returns a `ContainerTop`instance.
public func processes(_ nameOrId: String, psArgs: String = "-ef") async throws -> ContainerTop {
return try await client.run(ContainerTopEndpoint(nameOrId: nameOrId, psArgs: psArgs))
}

/// Returns a stream of metrics about a running container.
/// - Parameters:
/// - nameOrId: Name or ID of the`Container`.
/// - stream: Whether to continuously poll the container for metrics and stream them.
/// - oneShot: Set to `true`to only get a single stat instead of waiting for 2 cycles. Must be used with `stream`=`false`.
/// - Throws: Errors that can occur when executing the request.
/// - Returns: Returns a stream of `ContainerStats`instances.
public func stats(_ nameOrId: String, stream: Bool = true, oneShot: Bool = false) async throws -> AsyncThrowingStream<ContainerStats, Error> {
let endpoint = GetContainerStatsEndpoint(nameOrId: nameOrId, stream: stream, oneShot: oneShot)
let stream = try await client.run(endpoint, timeout: .hours(12), hasLengthHeader: false, separators: [UInt8(13)])
return try await endpoint.map(response: stream, as: ContainerStats.self)
}
}
}

Loading

0 comments on commit 35f6bf0

Please sign in to comment.