Commit graph

166 commits

Author SHA1 Message Date
Sebastiaan van Stijn
d492101172
daemon: SystemInfo() extract collecting debugging information to a helper
This makes it more inline with other data we collect, and can be used to make
some info optional at some point.

fillDebugInfo sets the current debugging state of the daemon, and additional
debugging information, such as the number of Go-routines, and file descriptors.

Note that this currently always collects the information, but the CLI only
prints it if the daemon has debug enabled. We should consider to either make
this information optional (cli to request "with debugging information"), or
only collect it if the daemon has debug enabled. For the CLI code, see
https://github.com/docker/cli/blob/v20.10.12/cli/command/system/info.go#L239-L244

Additional note: the CLI considers info.SystemTime debugging information. This
felt a bit "odd" (daemon time could be useful for standard use), so I left this
out of this function.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2022-03-02 22:19:24 +01:00
Sebastiaan van Stijn
ac2cd5a8f2
daemon: unexport Daemon.ID and Daemon.RegistryService
These are used internally only, and set by daemon.NewDaemon(). If they're
used externally, we should add an accessor added (which may be something
we want to do for daemon.registryService (which should be its own backend)

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2022-03-02 22:19:22 +01:00
Sebastiaan van Stijn
a27f8aecad
daemon: SystemInfo() extract container counts to a helper function
This makes it more inline with other data we collect, and can be used to
make some info optional at some point.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2022-03-02 22:19:20 +01:00
Sebastiaan van Stijn
705f9b68cc
some cleaning up of isolation checks, and platform information
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2022-02-18 22:58:37 +01:00
Sebastiaan van Stijn
ff2a5301b8
daemon: remove discovery-related config handling
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2022-01-06 18:28:17 +01:00
Anca Iordache
427c7cc5f8
Add http(s) proxy properties to daemon configuration
This allows configuring the daemon's proxy server through the daemon.json con-
figuration file or command-line flags configuration file, in addition to the
existing option (through environment variables).

Configuring environment variables on Windows to configure a service is more
complicated than on Linux, and adding alternatives for this to the daemon con-
figuration makes the configuration more transparent and easier to use.

The configuration as set through command-line flags or through the daemon.json
configuration file takes precedence over env-vars in the daemon's environment,
which allows the daemon to use a different proxy. If both command-line flags
and a daemon.json configuration option is set, an error is produced when starting
the daemon.

Note that this configuration is not "live reloadable" due to Golang's use of
`sync.Once()` for proxy configuration, which means that changing the proxy
configuration requires a restart of the daemon (reload / SIGHUP will not update
the configuration.

With this patch:

    cat /etc/docker/daemon.json
    {
        "http-proxy": "http://proxytest.example.com:80",
        "https-proxy": "https://proxytest.example.com:443"
    }

    docker pull busybox
    Using default tag: latest
    Error response from daemon: Get "https://registry-1.docker.io/v2/": proxyconnect tcp: dial tcp: lookup proxytest.example.com on 127.0.0.11:53: no such host

    docker build .
    Sending build context to Docker daemon  89.28MB
    Step 1/3 : FROM golang:1.16-alpine AS base
    Get "https://registry-1.docker.io/v2/": proxyconnect tcp: dial tcp: lookup proxytest.example.com on 127.0.0.11:53: no such host

Integration tests were added to test the behavior:

- verify that the configuration through all means are used (env-var,
  command-line flags, damon.json), and used in the expected order of
  preference.
- verify that conflicting options produce an error.

Signed-off-by: Anca Iordache <anca.iordache@docker.com>
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-10-27 12:38:59 +02:00
Sebastiaan van Stijn
a6ce7eff65
daemon: move maskCredentials to config package
This allows the utility to be used in other places.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-10-27 12:38:56 +02:00
Sebastiaan van Stijn
27aaadb710
daemon: normalize seccomp profile as part of setupSeccompProfile()
This makes sure that the value set in the daemon can be used as-is,
without having to replicate the normalization logic elsewhere.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-08-07 15:41:46 +02:00
Sebastiaan van Stijn
04f932ac86
daemon: move custom seccomp profile warning from CLI to daemon side
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-08-07 15:41:44 +02:00
Sebastiaan van Stijn
ee02257553
Add const for "unconfined" and default seccomp profiles
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-08-07 15:36:06 +02:00
Sebastiaan van Stijn
9b795c3e50
pkg/sysinfo.New(), daemon.RawSysInfo(): remove "quiet" argument
The "quiet" argument was only used in a single place (at daemon startup), and
every other use had to pass "false" to prevent this function from logging
warnings.

Now that SysInfo contains the warnings that occurred when collecting the
system information, we can make leave it up to the caller to use those
warnings (and log them if wanted).

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-07-14 23:10:07 +02:00
Sebastiaan van Stijn
dc7cbb9b33
remove layerstore indexing by OS (used for LCOW)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-06-10 17:49:11 +02:00
Sebastiaan van Stijn
076d9c6037
daemon: remove graphdriver indexing by OS (used for LCOW)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-06-10 10:17:26 +02:00
Sebastiaan van Stijn
328de0b8d9
Update documentation links
- Using "/go/" redirects for some topics, which allows us to
  redirect to new locations if topics are moved around in the
  documentation.
- Updated some old URLs to their new location.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-02-25 12:11:50 +01:00
Sebastiaan van Stijn
1c0af18c6c
vendor: opencontainers/selinux v1.8.0, and remove selinux build-tag and stubs
full diff: https://github.com/opencontainers/selinux/compare/v1.7.0...v1.8.0

Remove "selinux" build tag

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2020-12-24 00:47:16 +01:00
Brian Goff
5f5285a6e2 Sterner warnings for unathenticated tcp
People keep doing this and getting pwned because they accidentally left
it exposed to the internet.

The warning about doing this has been there forever.
This introduces a sleep after warning.
To disable the extra sleep users must explicitly specify `--tls=false`
or `--tlsverify=false`

Warning also specifies this sleep will be removed in the next release
where the flag will be required if running unauthenticated.

Signed-off-by: Brian Goff <cpuguy83@gmail.com>
2020-09-25 00:21:54 +00:00
Wang Yumu
840a12ac90 Add DefaultAddressPools to docker info #40388
Signed-off-by: Wang Yumu <37442693@qq.com>
2020-07-08 00:53:11 +08:00
Brian Goff
f63f73a4a8 Configure shims from runtime config
In dockerd we already have a concept of a "runtime", which specifies the
OCI runtime to use (e.g. runc).
This PR extends that config to add containerd shim configuration.
This option is only exposed within the daemon itself (cannot be
configured in daemon.json).
This is due to issues in supporting unknown shims which will require
more design work.

What this change allows us to do is keep all the runtime config in one
place.

So the default "runc" runtime will just have it's already existing shim
config codified within the runtime config alone.
I've also added 2 more "stock" runtimes which are basically runc+shimv1
and runc+shimv2.
These new runtime configurations are:

- io.containerd.runtime.v1.linux - runc + v1 shim using the V1 shim API
- io.containerd.runc.v2 - runc + shim v2

These names coincide with the actual names of the containerd shims.

This allows the user to essentially control what shim is going to be
used by either specifying these as a `--runtime` on container create or
by setting `--default-runtime` on the daemon.

For custom/user-specified runtimes, the default shim config (currently
shim v1) is used.

Signed-off-by: Brian Goff <cpuguy83@gmail.com>
2020-07-13 14:18:02 -07:00
Akihiro Suda
f350b53241 cgroup2: implement docker info
ref: https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html

Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
2020-04-17 07:20:01 +09:00
Akihiro Suda
f073ed4187
Merge pull request #39816 from tao12345666333/rm-systeminfo-error-handler
Remove `SystemInfo()` error handling.
2020-03-13 01:59:13 +09:00
Sebastiaan van Stijn
a5538c06f9
Add warning about deprecated "cluster" options to "docker info"
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2020-03-03 18:10:31 +01:00
Sebastiaan van Stijn
0f0e3163b5
daemon/info: remove use of docker/go-connections
The `docker/go-connections` package was only used for a quite generic utility.

This patch removes the use of the package by replacing the `GetProxyEnv` utility with
a local function that's based on the one in golang.org/x/net/http/httpproxy:
c21de06aaf/http/httpproxy/proxy.go (L100-L107)

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2019-09-25 11:00:40 +02:00
Sebastiaan van Stijn
07ff4f1de8
goimports: fix imports
Format the source according to latest goimports.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2019-09-18 12:56:54 +02:00
Jintao Zhang
9134130b39 Remove SystemInfo() error handling.
Signed-off-by: Jintao Zhang <zhangjintao9020@gmail.com>
2019-08-29 07:44:39 +08:00
Jean Rouge
d363a1881e Adding OS version info to the nodes' Info struct
This is needed so that we can add OS version constraints in Swarmkit, which
does require the engine to report its host's OS version (see
https://github.com/docker/swarmkit/issues/2770).

The OS version is parsed from the `os-release` file on Linux, and from the
`ReleaseId` string value of the `SOFTWARE\Microsoft\Windows NT\CurrentVersion`
registry key on Windows.

Added unit tests when possible, as well as Prometheus metrics.

Signed-off-by: Jean Rouge <rougej+github@gmail.com>
2019-06-06 22:40:10 +00:00
Rob Gulewich
072400fc4b Make cgroup namespaces configurable
This adds both a daemon-wide flag and a container creation property:
- Set the `CgroupnsMode: "host|private"` HostConfig property at
  container creation time to control what cgroup namespace the container
  is created in
- Set the `--default-cgroupns-mode=host|private` daemon flag to control
  what cgroup namespace containers are created in by default
- Set the default if the daemon flag is unset to "host", for backward
  compatibility
- Default to CgroupnsMode: "host" for client versions < 1.40

Signed-off-by: Rob Gulewich <rgulewich@netflix.com>
2019-05-07 10:22:16 -07:00
Tonis Tiigi
f9b9d5f584 builder-next: fixes for rootless mode
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2019-02-28 10:44:21 -08:00
Akihiro Suda
ec87479b7e allow running dockerd in an unprivileged user namespace (rootless mode)
Please refer to `docs/rootless.md`.

TLDR:
 * Make sure `/etc/subuid` and `/etc/subgid` contain the entry for you
 * `dockerd-rootless.sh --experimental`
 * `docker -H unix://$XDG_RUNTIME_DIR/docker.sock run ...`

Signed-off-by: Akihiro Suda <suda.akihiro@lab.ntt.co.jp>
2019-02-04 00:24:27 +09:00
Sebastiaan van Stijn
2137b8ccf2
Add containerd, runc, and docker-init versions to /version
This patch adds version information about the containerd,
runc, and docker-init components to the /version endpoint.

With this patch applied, running:

```
curl --unix-socket /var/run/docker.sock http://localhost/version | jq .
```

Will produce this response:

```json
{
  "Platform": {
    "Name": ""
  },
  "Components": [
    {
      "Name": "Engine",
      "Version": "dev",
      "Details": {
        "ApiVersion": "1.40",
        "Arch": "amd64",
        "BuildTime": "2018-11-08T10:23:42.000000000+00:00",
        "Experimental": "false",
        "GitCommit": "7d02782d2f",
        "GoVersion": "go1.11.2",
        "KernelVersion": "4.9.93-linuxkit-aufs",
        "MinAPIVersion": "1.12",
        "Os": "linux"
      }
    },
    {
      "Name": "containerd",
      "Version": "v1.1.4",
      "Details": {
        "GitCommit": "9f2e07b1fc1342d1c48fe4d7bbb94cb6d1bf278b"
      }
    },
    {
      "Name": "runc",
      "Version": "1.0.0-rc5+dev",
      "Details": {
        "GitCommit": "a00bf0190895aa465a5fbed0268888e2c8ddfe85"
      }
    },
    {
      "Name": "docker-init",
      "Version": "0.18.0",
      "Details": {
        "GitCommit": "fec3683"
      }
    }
  ],
  "Version": "dev",
  "ApiVersion": "1.40",
  "MinAPIVersion": "1.12",
  "GitCommit": "7d02782d2f",
  "GoVersion": "go1.11.2",
  "Os": "linux",
  "Arch": "amd64",
  "KernelVersion": "4.9.93-linuxkit-aufs",
  "BuildTime": "2018-11-08T10:23:42.000000000+00:00"
}
```

When using a recent version of the CLI, that information is included in the
output of `docker version`:

```
Client: Docker Engine - Community
 Version:           18.09.0
 API version:       1.39
 Go version:        go1.10.4
 Git commit:        4d60db4
 Built:             Wed Nov  7 00:46:51 2018
 OS/Arch:           linux/amd64
 Experimental:      false

Server:
 Engine:
  Version:          dev
  API version:      1.40 (minimum version 1.12)
  Go version:       go1.11.2
  Git commit:       7d02782d2f
  Built:            Thu Nov  8 10:23:42 2018
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          v1.1.4
  GitCommit:        9f2e07b1fc1342d1c48fe4d7bbb94cb6d1bf278b
 runc:
  Version:          1.0.0-rc5+dev
  GitCommit:        a00bf0190895aa465a5fbed0268888e2c8ddfe85
 docker-init:
  Version:          0.18.0
  GitCommit:        fec3683
```

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2019-01-14 23:27:05 +01:00
Sebastiaan van Stijn
13ef0ebd2b
Deprecate AuFS storage driver, and add warning
The `aufs` storage driver is deprecated in favor of `overlay2`, and will
be removed in a future release. Users of the `aufs` storage driver are
recommended to migrate to a different storage driver, such as `overlay2`, which
is now the default storage driver.

The `aufs` storage driver facilitates running Docker on distros that have no
support for OverlayFS, such as Ubuntu 14.04 LTS, which originally shipped with
a 3.14 kernel.

Now that Ubuntu 14.04 is no longer a supported distro for Docker, and `overlay2`
is available to all supported distros (as they are either on kernel 4.x, or have
support for multiple lowerdirs backported), there is no reason to continue
maintenance of the `aufs` storage driver.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2018-10-26 18:41:46 +02:00
Sebastiaan van Stijn
31be4e0ba1
Deprecate legacy overlay storage driver, and add warning
The `overlay` storage driver is deprecated in favor of the `overlay2` storage
driver, which has all the benefits of `overlay`, without its limitations (excessive
inode consumption). The legacy `overlay` storage driver will be removed in a future
release. Users of the `overlay` storage driver should migrate to the `overlay2`
storage driver.

The legacy `overlay` storage driver allowed using overlayFS-backed filesystems
on pre 4.x kernels. Now that all supported distributions are able to run `overlay2`
(as they are either on kernel 4.x, or have support for multiple lowerdirs
backported), there is no reason to keep maintaining the `overlay` storage driver.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2018-10-11 15:49:15 +02:00
Sebastiaan van Stijn
06fcabbaa0
Deprecate "devicemapper" storage driver, and add warning
The `devicemapper` storage driver is deprecated in favor of `overlay2`, and will
be removed in a future release. Users of the `devicemapper` storage driver are
recommended to migrate to a different storage driver, such as `overlay2`, which
is now the default storage driver.

The `devicemapper` storage driver facilitates running Docker on older (3.x) kernels
that have no support for other storage drivers (such as overlay2, or AUFS).

Now that support for `overlay2` is added to all supported distros (as they are
either on kernel 4.x, or have support for multiple lowerdirs backported), there
is no reason to continue maintenance of the `devicemapper` storage driver.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2018-10-11 15:46:26 +02:00
Dani Louca
78fd978454 Masking credentials from proxy URL
Signed-off-by: Dani Louca <dani.louca@docker.com>
2018-10-01 14:06:00 -04:00
Sebastiaan van Stijn
547b993e07
Add warning if REST API is accessible through an insecure connection
The remote API allows full privilege escalation and is equivalent to
having root access on the host. Because of this, the API should never
be accessible through an insecure connection (TCP without TLS, or TCP
without TLS  verification).

Although a warning is already logged on startup if the daemon uses an
insecure configuration, this warning is not very visible (unless someone
decides to read the logs).

This patch attempts to make insecure configuration more visible by sending
back warnings through the API (which will be printed when using `docker info`).

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2018-08-21 22:03:24 +02:00
Sebastiaan van Stijn
a3d4238b9c
Add "Warnings" to /info endpoint, and move detection to the daemon
When requesting information about the daemon's configuration through the `/info`
endpoint, missing features (or non-recommended settings) may have to be presented
to the user.

Detecting these situations, and printing warnings currently is handled by the
cli, which results in some complications:

- duplicated effort: each client has to re-implement detection and warnings.
- it's not possible to generate warnings for reasons outside of the information
  returned in the `/info` response.
- cli-side detection has to be updated for new conditions. This means that an
  older cli connecting to a new daemon may not print all warnings (due to
  it not detecting the new conditions)
- some warnings (in particular, warnings about storage-drivers) depend on
  driver-status (`DriverStatus`) information. The format of the information
  returned in this field is not part of the API specification and can change
  over time, resulting in cli-side detection no longer being functional.

This patch adds a new `Warnings` field to the `/info` response. This field is
to return warnings to be presented by the user.

Existing warnings that are currently handled by the CLI are copied to the daemon
as part of this patch; This change is backward-compatible with existing
clients; old client can continue to use the client-side warnings, whereas new
clients can skip client-side detection, and print warnings that are returned by
the daemon.

Example response with this patch applied;

```bash
curl --unix-socket /var/run/docker.sock http://localhost/info | jq .Warnings
```

```json
[
  "WARNING: bridge-nf-call-iptables is disabled",
  "WARNING: bridge-nf-call-ip6tables is disabled"
]
```

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2018-08-21 11:36:15 +02:00
Daniel Hiltgen
896d1b1c61 Expose license status in Info (#37612)
* Expose license status in Info

This wires up a new field in the Info payload that exposes the license.
For moby this is hardcoded to always report a community edition.
Downstream enterprise dockerd will have additional licensing logic wired
into this function to report details about the current license status.

Signed-off-by: Daniel Hiltgen <daniel.hiltgen@docker.com>

* Code review comments

Signed-off-by: Daniel Hiltgen <daniel.hiltgen@docker.com>

* Add windows autogen support

Signed-off-by: Daniel Hiltgen <daniel.hiltgen@docker.com>
2018-08-17 17:05:21 -07:00
John Stephens
b3e9f7b13b
Merge pull request #35521 from salah-khan/35507
Add --chown flag support for ADD/COPY commands for Windows
2018-08-17 11:31:16 -07:00
Salahuddin Khan
763d839261 Add ADD/COPY --chown flag support to Windows
This implements chown support on Windows. Built-in accounts as well
as accounts included in the SAM database of the container are supported.

NOTE: IDPair is now named Identity and IDMappings is now named
IdentityMapping.

The following are valid examples:
ADD --chown=Guest . <some directory>
COPY --chown=Administrator . <some directory>
COPY --chown=Guests . <some directory>
COPY --chown=ContainerUser . <some directory>

On Windows an owner is only granted the permission to read the security
descriptor and read/write the discretionary access control list. This
fix also grants read/write and execute permissions to the owner.

Signed-off-by: Salahuddin Khan <salah@docker.com>
2018-08-13 21:59:11 -07:00
Sebastiaan van Stijn
e6e8ab50fa
Do not return "<unknown>" in /info response
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2018-07-16 16:09:58 +02:00
Sebastiaan van Stijn
c03d3a416b
Unexport daemon.FillPlatformInfo
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2018-07-13 13:16:34 +02:00
Sebastiaan van Stijn
50eaed4d0c
Refactor daemon.info to reduce cyclomatic complexity
Before this change;

    gocyclo daemon/info.go
    17 daemon (*Daemon).SystemInfo daemon/info.go:27:1
    2 daemon (*Daemon).SystemVersion daemon/info.go:150:1
    1 daemon (*Daemon).showPluginsInfo daemon/info.go:195:1

After this change;

    gocyclo daemon/info.go
    8 daemon (*Daemon).fillSecurityOptions daemon/info.go:150:1
    5 daemon operatingSystem daemon/info.go:201:1
    3 daemon (*Daemon).fillDriverInfo daemon/info.go:121:1
    2 daemon hostName daemon/info.go:172:1
    2 daemon memInfo daemon/info.go:192:1
    2 daemon kernelVersion daemon/info.go:182:1
    1 daemon (*Daemon).SystemVersion daemon/info.go:81:1
    1 daemon (*Daemon).SystemInfo daemon/info.go:27:1
    1 daemon (*Daemon).fillPluginsInfo daemon/info.go:138:1

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2018-07-13 13:10:05 +02:00
Brian Goff
977109d808 Remove use of global volume driver store
Instead of using a global store for volume drivers, scope the driver
store to the caller (e.g. the volume store). This makes testing much
simpler.

Signed-off-by: Brian Goff <cpuguy83@gmail.com>
2018-04-17 14:07:08 -04:00
Daniel Nephin
2b1a2b10af Move ImageService to new package
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2018-02-26 16:49:37 -05:00
Daniel Nephin
0dab53ff3c Move all daemon image methods into imageService
imageService provides the backend for the image API and handles the
imageStore, and referenceStore.

Signed-off-by: Daniel Nephin <dnephin@docker.com>
2018-02-26 16:48:29 -05:00
Daniel Nephin
4f0d95fa6e Add canonical import comment
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2018-02-05 16:51:57 -05:00
John Howard
afd305c4b5 LCOW: Refactor to multiple layer-stores based on feedback
Signed-off-by: John Howard <jhoward@microsoft.com>
2018-01-18 08:31:05 -08:00
John Howard
ce8e529e18 LCOW: Re-coalesce stores
Signed-off-by: John Howard <jhoward@microsoft.com>

The re-coalesces the daemon stores which were split as part of the
original LCOW implementation.

This is part of the work discussed in https://github.com/moby/moby/issues/34617,
in particular see the document linked to in that issue.
2018-01-18 08:29:19 -08:00
Tibor Vass
9152e63290 api: generalize version information to any platform component
This change adds a Platform struct with a Name field and a general
Components field to the Version API type. This will allow API
consumers to show version information for the whole platform and
it will allow API providers to set the versions for the various
components of the platform.

All changes here are backwards compatible.

Signed-off-by: Tibor Vass <tibor@docker.com>
2017-12-06 23:23:55 +01:00
Daniel Nephin
2f5f0af3fd Add unconvert linter
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2017-08-24 15:08:31 -04:00
Derek McGowan
1009e6a40b
Update logrus to v1.0.1
Fixes case sensitivity issue

Signed-off-by: Derek McGowan <derek@mcgstyle.net>
2017-07-31 13:16:46 -07:00