mirror of https://github.com/docker/docs.git
Initial import of https://github.com/docker/machine
This commit is contained in:
commit
29f7d3396b
|
|
@ -0,0 +1,4 @@
|
|||
docker-machine*
|
||||
*.log
|
||||
bin
|
||||
cover
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
OSS-LICENSES
|
||||
.DS_Store
|
||||
shell/shell
|
||||
shell/shell-history
|
||||
shell/out.txt
|
||||
shell/shell-config.json
|
||||
shell/shell-env.json
|
||||
shell9pAPI/shell9pAPI
|
||||
shell9pAPI/sftp/sftp
|
||||
shell/sftpKeys
|
||||
shellAgent/shellAgent
|
||||
shellAgent/hostKey.pem
|
||||
shell/key.pem
|
||||
shell/key.pub
|
||||
agentSDK/node/node_modules/*
|
||||
!agentSDK/node/node_modules/dockersdk
|
||||
agentSDK/lib/agentlib.dylib
|
||||
agentSDK/node/key.pem
|
||||
agentSDK/node/keyC.pem
|
||||
agentSDK/node/keyNode.pem
|
||||
_tests
|
||||
all-packages.txt
|
||||
|
||||
# ignore compiled binaries
|
||||
v1/agent/agent
|
||||
v1/agent/com.docker.agent.exe
|
||||
v1/cmd/proxy/proxy.exe
|
||||
|
||||
# ignore generated SSH keys
|
||||
v1/clitool/key.pem
|
||||
v1/agent/hostKey.pem
|
||||
|
||||
# ignore files generated by cli testing
|
||||
v1/agent/application_storage
|
||||
v1/bin
|
||||
v1/cmd/agent/agent
|
||||
|
||||
v1/db/irmin/_build
|
||||
v1/db/irmin9p/irmin9p
|
||||
v1/vendor/github.com/mortdeus/go9p/srv/examples/ufs/ufs
|
||||
|
||||
# ignore compiled boot2docker, since we will rebuild it
|
||||
v1/boot2docker/boot2docker-data.img.tar
|
||||
v1/boot2docker/vmlinuz64
|
||||
v1/boot2docker/initrd.img
|
||||
v1/devtool_sdk/libsrpc/libsrpc
|
||||
v1/devtool_sdk/libsrpc/libsrpc.dylib
|
||||
v1/devtool_sdk/libsrpc/libsrpc.h
|
||||
|
||||
# ignore temporary files from editors
|
||||
*.swp
|
||||
*~
|
||||
*.sdf
|
||||
*.VC.opendb
|
||||
|
||||
_cache
|
||||
|
||||
# PLEASE DON'T PUT SUBPROJECT IGNORE PATTERNS HERE.
|
||||
# PUT THEM IN THE SUBPROJECT'S .gitignore FILE.
|
||||
# THIS MAKES MAINTENANCE EASIER. THANK YOU.
|
||||
v1/mac/dependencies
|
||||
v1/mac/bin/Windows/Dependencies/HyperVInstaller.ps1
|
||||
13
.travis.yml
13
.travis.yml
|
|
@ -1,13 +0,0 @@
|
|||
sudo: false
|
||||
|
||||
language: node_js
|
||||
node_js:
|
||||
- "4.2.2"
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- node_modules
|
||||
|
||||
script:
|
||||
- npm install
|
||||
- npm test
|
||||
|
|
@ -0,0 +1,629 @@
|
|||
# Changelog
|
||||
|
||||
# 0.8.0 (2016-6-14)
|
||||
|
||||
General
|
||||
- Fix issue with plugin heartbeat log repeating on disconnect
|
||||
- Add `tcsh` support to `env --shell`
|
||||
- Add `zsh` completion scripts
|
||||
- Bump Go version to 1.6.2
|
||||
|
||||
Drivers
|
||||
- `amazonec2`
|
||||
- Workaround to prevent orphaned SSH keys
|
||||
- `virtualbox`
|
||||
- Add option for VM UI type (`--virtualbox-ui-type`)
|
||||
- `vmwarefusion`
|
||||
- Fix CPU option inconsistency
|
||||
- `openstack`
|
||||
- Expose user data parameter (`--openstack-user-data-file`)
|
||||
- `generic`
|
||||
- Copy public key to created Machine directory
|
||||
|
||||
Provisioners
|
||||
- Add Oracle Enterprise Linux support
|
||||
- Fix port binding of Swarm master
|
||||
- Add ability to create a manager instance which does not get scheduled on
|
||||
- Introduce `--swarm-join-opt` to pass options to agent nodes
|
||||
- Various SSH-related fixes
|
||||
- Fix state for upgrade path
|
||||
|
||||
# 0.7.0 (2016-4-13)
|
||||
|
||||
General
|
||||
- `DRIVER` environment variable now supported to supply value for `create --driver` flag
|
||||
- Update to Go 1.6.1
|
||||
- SSH client has been refactored
|
||||
- RC versions of Machine will now create and upgrade to boot2docker RCs instead
|
||||
of stable versions if available
|
||||
|
||||
Drivers
|
||||
- `azure`
|
||||
- Driver has been completely re-written to use resource templates and a significantly easier-to-use authentication model
|
||||
- `digitalocean`
|
||||
- New `--digitalocean-ssh-key-fingerprint` for using existing SSH keys instead of creating new ones
|
||||
- `virtualbox`
|
||||
- Fix issue with `bootlocal.sh`
|
||||
- New `--virtualbox-nictype` flag to set driver for NAT network
|
||||
- More robust host-only interface collision detection
|
||||
- Add support for running VirtualBox on a Windows 32 bit host
|
||||
- Change default DNS passthrough handling
|
||||
- `amazonec2`
|
||||
- Specifying multiple security groups to use is now supported
|
||||
- `exoscale`
|
||||
- Add support for user-data
|
||||
- `hyperv`
|
||||
- Machines can now be created by a non-administrator
|
||||
- `rackspace`
|
||||
- New `--rackspace-active-timeout` parameter
|
||||
- `vmwarefusion`
|
||||
- Bind mount shared folder directory by default
|
||||
- `google`
|
||||
- New `--google-use-internal-ip-only` parameter
|
||||
|
||||
Provisioners
|
||||
- General
|
||||
- Support for specifying Docker engine port in some cases
|
||||
- CentOS
|
||||
- Now defaults to using upstream `get.docker.com` script instead of custom RPMs.
|
||||
- boot2docker
|
||||
- More robust eth* interface detection
|
||||
- Swarm
|
||||
- Add `--swarm-experimental` parameter to enable experimental Swarm features
|
||||
|
||||
|
||||
# 0.6.0 (2016-02-04)
|
||||
|
||||
+ Fix SSH wait before provisioning issue
|
||||
|
||||
# 0.6.0-rc4 (2016-02-03)
|
||||
|
||||
General
|
||||
|
||||
+ `env`
|
||||
+ Fix shell auto detection
|
||||
|
||||
Drivers
|
||||
|
||||
+ `exoscale`
|
||||
+ Fix configuration of exoscale endpoint
|
||||
|
||||
# 0.6.0-rc3 (2016-02-01)
|
||||
|
||||
- Exit with code 3 if error is during pre-create check
|
||||
|
||||
# 0.6.0-rc2 (2016-01-28)
|
||||
|
||||
- Fix issue creating Swarms
|
||||
- Fix `ls` header issue
|
||||
- Add code to wait for Docker daemon before returning from `start` / `restart`
|
||||
- Start porting integration tests to Go from BATS
|
||||
- Add Appveyor for Windows tests
|
||||
- Update CoreOS provisioner to use `docker daemon`
|
||||
- Various documentation and error message fixes
|
||||
- Add ability to create GCE machine using existing VM
|
||||
|
||||
# 0.6.0-rc1 (2016-01-18)
|
||||
|
||||
General
|
||||
|
||||
- Update to Go 1.5.3
|
||||
- Short form of command invocations is now supported
|
||||
- `docker-machine start`, `docker-machine stop` and others will now use
|
||||
`default` as the machine name argument if one is not specified
|
||||
- Fix issue with panics in drivers
|
||||
- Machine now returns exit code 3 if the pre-create check fails.
|
||||
- This is potentially useful for scripting `docker-machine`.
|
||||
- `docker-machine provision` command added to allow re-running of provisioning
|
||||
on instances.
|
||||
- This allows users to re-run provisioning if it fails during `create`
|
||||
instead of needing to completely start over.
|
||||
|
||||
Provisioning
|
||||
|
||||
- Most provisioners now use `docker daemon` instead of `docker -d`
|
||||
- Swarm masters now run with replication enabled
|
||||
- If `/var/lib` is a BTRFS partition, `btrfs` will now be used as the storage
|
||||
driver for the instance
|
||||
|
||||
Drivers
|
||||
|
||||
- Amazon EC2
|
||||
- Default VPC will be used automatically if none is specified
|
||||
- Credentials are now be read from the conventional `~/.aws/credentials`
|
||||
file automatically
|
||||
- Fix a few issues such as nil pointer dereferences
|
||||
- VMware Fusion
|
||||
- Try to get IP from multiple DHCP lease files
|
||||
- OpenStack
|
||||
- Only derive tenant ID if tenant name is supplied
|
||||
|
||||
# 0.5.6 (2016-01-11)
|
||||
|
||||
General
|
||||
|
||||
- `create`
|
||||
- Set swarm master to advertise on port 3376
|
||||
- Fix swarm restart policy
|
||||
- Stop asking for ssh key passwords interactively
|
||||
- `env`
|
||||
- Improve documentation
|
||||
- Fix bash on windows
|
||||
- Automatic shell detection on Windows
|
||||
- `help`
|
||||
- Don't show the full path to `docker-machine.exe` on windows
|
||||
- `ls`
|
||||
- Allow custom format
|
||||
- Improve documentation
|
||||
- `restart`
|
||||
- Improve documentation
|
||||
- `rm`
|
||||
- Improve documentation
|
||||
- Better user experience when removing multiple hosts
|
||||
- `version`
|
||||
- Don't show the full path to `docker-machine.exe` on windows
|
||||
- `start`, `stop`, `restart`, `kill`
|
||||
- Better logs and homogeneous behaviour across all drivers
|
||||
|
||||
Build
|
||||
|
||||
- Introduce CI tests for external binary compatibility
|
||||
- Add amazon EC2 integration test
|
||||
|
||||
Misc
|
||||
|
||||
- Improve BugSnags reports: better shell detection, better windows version detection
|
||||
- Update DockerClient dependency
|
||||
- Improve bash-completion script
|
||||
- Improve documentation for bash-completion
|
||||
|
||||
Drivers
|
||||
|
||||
- Amazon EC2
|
||||
- Improve documentation
|
||||
- Support optional tags
|
||||
- Option to create EbsOptimized instances
|
||||
- Google
|
||||
- Fix remove when instance is stopped
|
||||
- Openstack
|
||||
- Flags to import and reuse existing nova keypairs
|
||||
- VirtualBox
|
||||
- Fix multiple bugs related to host-only adapters
|
||||
- Retry commands when `VBoxManage` is not ready
|
||||
- Reject VirtualBox versions older that 4.3
|
||||
- Fail with a clear message when Hyper-v installation prevents VirtualBox from working
|
||||
- Print a warning for Boot2Docker v1.9.1, which is known to have an issue with AUFS
|
||||
- Vmware Fusion
|
||||
- Support soft links in VM paths
|
||||
|
||||
Libmachine
|
||||
|
||||
- Fix code sample that uses libmachine
|
||||
- libmachine can be used in external applications
|
||||
|
||||
|
||||
# 0.5.5 (2015-12-28)
|
||||
|
||||
General
|
||||
|
||||
- `env`
|
||||
- Better error message if swarm is down
|
||||
- Add quotes to command if there are spaces in the path
|
||||
- Fix Powershell env hints
|
||||
- Default to cmd shell on windows
|
||||
- Detect fish shell
|
||||
- `scp`
|
||||
- Ignore empty ssh key
|
||||
- `stop`, `start`, `kill`
|
||||
- Add feedback to the user
|
||||
- `rm`
|
||||
- Now works when `config.json` is not found
|
||||
- `ssh`
|
||||
- Disable ControlPath
|
||||
- Log which SSH client is used
|
||||
- `ls`
|
||||
- Listing is now faster by reducing calls to the driver
|
||||
- Shows if the active machine is a swarm cluster
|
||||
|
||||
Build
|
||||
|
||||
- Automate 90% of the release process
|
||||
- Upgrade to Go 1.5.2
|
||||
- Don't build 32bits binaries for Linux and OSX
|
||||
- Prevent makefile from defaulting to using containers
|
||||
|
||||
Misc
|
||||
|
||||
- Update docker-machine version
|
||||
- Updated the bash completion with new options added
|
||||
- Bugsnag: Retrieve windows version on non-english OS
|
||||
|
||||
Drivers
|
||||
|
||||
- Amazon EC2
|
||||
- Convert API calls to official SDK
|
||||
- Make DeviceName configurable
|
||||
- Digital Ocean
|
||||
- Custom SSH port support
|
||||
- Generic
|
||||
- Don't support `kill` since `stop` is not supported
|
||||
- Google
|
||||
- Coreos provisionning
|
||||
- Hyper-V
|
||||
- Lot's of code simplifications
|
||||
- Pre-Check that the user is an Administrator
|
||||
- Pre-Check that the virtual switch exists
|
||||
- Add Environment variables for each flag
|
||||
- Fix how Powershell is detected
|
||||
- VSwitch name should be saved to config.json
|
||||
- Add a flag to set the CPU count
|
||||
- Close handle after copying boot2docker.iso into vm folder - will otherwise keep hyper-v from starting vm
|
||||
- Update Boot2Docker cache in PreCreateCheck phase
|
||||
- OpenStack
|
||||
- Filter floating IPs by tenant ID
|
||||
- Virtualbox
|
||||
- Reject duplicate hostonlyifs Name/IP with clear message
|
||||
- Detect when hostonlyif can't be created. Point to known working version of VirtualBox
|
||||
- Don't create the VM if no hardware virtualization is available and add a flag to force create
|
||||
- Add `VBox.log` to bugsnag crashreport
|
||||
- Update Boot2Docker cache in PreCreateCheck phase
|
||||
- Detect Incompatibility with Hyper-v
|
||||
- VSphere
|
||||
- Rewrite driver to work with govmomi instead of wrapping govc
|
||||
- All
|
||||
- Change host restart to use the driver implementation
|
||||
- Fix truncated logs
|
||||
- Increase heartbeat interval and timeout
|
||||
|
||||
Provisioners
|
||||
|
||||
- Download latest Boot2Docker if it is out-of-date
|
||||
- Add swarm config to coreos
|
||||
- All provisioners now honor `engine-install-url`
|
||||
|
||||
# 0.5.4 (2015-12-28)
|
||||
|
||||
This is a patch release to fix a regression with STDOUT/STDERR behavior (#2587).
|
||||
|
||||
# 0.5.3 (2015-12-14)
|
||||
|
||||
**Please note**: With this release Machine will be reverting back to distribution in a single binary, which is more efficient on bandwidth and hard disk space. All the core driver plugins are now included in the main binary. You will want to delete the old driver binaries that you might have in your path.
|
||||
|
||||
e.g.:
|
||||
|
||||
```console
|
||||
$ rm /usr/local/bin/docker-machine-driver-{amazonec2,azure,digitalocean,exoscale,generic,google,hyperv,none,openstack,rackspace,softlayer,virtualbox,vmwarefusion,vmwarevcloudair,vmwarevsphere}
|
||||
```
|
||||
|
||||
Non-core driver plugins should still work as intended (in externally distributed binaries of the form `docker-machine-driver-name`. Please report any issues you encounter them with externally loaded plugins.
|
||||
|
||||
General
|
||||
|
||||
- Optionally report crashes to Bugsnag to help us improve docker-machine
|
||||
- Fix multiple nil dereferences in `docker-machine ls` command
|
||||
- Improve the build and CI
|
||||
- `docker-machine env` now supports emacs
|
||||
- Run Swarm containers in provisioning step using Docker API instead of SSH/shell
|
||||
- Show docker daemon version in `docker-machine ls`
|
||||
- `docker-machine ls` can filter by engine label
|
||||
- `docker-machine ls` filters are case insensitive
|
||||
- `--timeout` flag for `docker-machine ls`
|
||||
- Logs use `logrus` library
|
||||
- Swarm container network is now `host`
|
||||
- Added advertise flag to Swarm manager template
|
||||
- Fix `help` flag for `docker-machine ssh`
|
||||
- Add confirmation `-y` flag to `docker-machine rm`
|
||||
- Fix `docker-machine config` for fish
|
||||
- Embed all core drivers in `docker-machine` binary to reduce the bundle from 120M to 15M
|
||||
|
||||
Drivers
|
||||
|
||||
- Generic
|
||||
- Support password protected ssh keys though ssh-agent
|
||||
- Support DNS names
|
||||
- Virtualbox
|
||||
- Show a warning if virtualbox is too old
|
||||
- Recognize yet another Hardware Virtualization issue pattern
|
||||
- Fix Hardware Virtualization on Linux/AMD
|
||||
- Add the `--virtualbox-host-dns-resolver` flag
|
||||
- Allow virtualbox DNSProxy override
|
||||
- Google
|
||||
- Open firewall port for Swarm when needed
|
||||
- VMware Fusion
|
||||
- Explicitly set umask before invoking vmrun in vmwarefusion
|
||||
- Activate the plugin only on OSX
|
||||
- Add id/gid option to mount when using vmhgfs
|
||||
- Fix for vSphere driver boot2docker ISO issues
|
||||
- Digital Ocean
|
||||
- Support for creating Droplets with Cloud-init User Data
|
||||
- Openstack
|
||||
- Sanitize keynames by replacing dots with underscores
|
||||
- All
|
||||
- Most base images are now set to `Ubuntu 15.10`
|
||||
- Fix compatibility with drivers developed with docker-machine 0.5.0
|
||||
- Better error report for broken/incompatible drivers
|
||||
- Don't break `config.json` configuration when the disk is full
|
||||
|
||||
Provisioners
|
||||
|
||||
- Increase timeout for installing boot2docker
|
||||
- Support `Ubuntu 15.10`
|
||||
|
||||
Misc
|
||||
|
||||
- Improve the documentation
|
||||
- Update known drivers list
|
||||
|
||||
# 0.5.2 (2015-11-30)
|
||||
|
||||
General
|
||||
|
||||
- Bash autocompletion and helpers fixed
|
||||
- Remove `RawDriver` from `config.json` - Driver parameters can now be edited
|
||||
directly again in this file.
|
||||
- Change fish `env` variable setting to be global
|
||||
- Add `docker-machine version` command
|
||||
- Move back to normal `codegangsta/cli` upstream
|
||||
- `--tls-san` flag for extra SANs
|
||||
|
||||
Drivers
|
||||
|
||||
- Fix `GetURL` IPv6 compatibility
|
||||
- Add documentation page for available 3rd party drivers
|
||||
- VirtualBox
|
||||
- Support for shared folders and virtualization detection on Linux hosts
|
||||
- Improved detection of invalid host-only interface settings
|
||||
- Google
|
||||
- Update default images
|
||||
- VMware Fusion
|
||||
- Add option to disable shared folder
|
||||
- Generic
|
||||
- New environment variables for flags
|
||||
|
||||
Provisioners
|
||||
|
||||
- Support for Ubuntu >=15.04. This means Ubuntu machines can be created which
|
||||
work with `overlay` driver of lib network.
|
||||
- Fix issue with current netstat / daemon availability checking
|
||||
|
||||
# 0.5.1 (2015-11-16)
|
||||
|
||||
- Fixed boot2docker VM import regression
|
||||
- Fix regression breaking `docker-machine env -u` to unset environment variables
|
||||
- Enhanced virtualization capability detection and `VBoxManage` path detection
|
||||
- Properly lock VirtualBox access when running several commands concurrently
|
||||
- Allow plugins to write to STDOUT without `--debug` enabled
|
||||
- Fix Rackspace driver regression
|
||||
- Support colons in `docker-machine scp` filepaths
|
||||
- Pass environment variables for provisioned Engines to Swarm as well
|
||||
- Various enhancements around boot2docker ISO upgrade (progress bar, increased timeout)
|
||||
|
||||
# 0.5.0 (2015-11-1)
|
||||
|
||||
- General
|
||||
- Add pluggable driver model
|
||||
- Clean up code to be more modular and reusable in `libmachine`
|
||||
- Add `--github-api-token` for situations where users are getting rate limited
|
||||
by GitHub attempting to get the current `boot2docker.iso` version
|
||||
- Various enhancements around the Makefile and build toolchain (still an active WIP)
|
||||
- Disable SSH multiplex explicitly in commands run with the "External" client
|
||||
- Show "-" for "inactive" machines instead of nothing
|
||||
- Make daemon status detection more robust
|
||||
- Provisioners
|
||||
- New CoreOS, SUSE, and Arch Linux provisioners
|
||||
- Fixes around package installation / upgrade code on Debian and Ubuntu
|
||||
- CLI
|
||||
- Support for regular expression pattern matching and matching by names in `ls --filter`
|
||||
- `--no-proxy` flag for `env` (sets `NO_PROXY` in addition to other environment variables)
|
||||
- Drivers
|
||||
- `openstack`
|
||||
- `--openstack-ip-version` parameter
|
||||
- `--openstack-active-timeout` parameter
|
||||
- `google`
|
||||
- fix destructive behavior of `start` / `stop`
|
||||
- `hyperv`
|
||||
- fix issues with PowerShell
|
||||
- `vmwarefusion`
|
||||
- some issues with shared folders fixed
|
||||
- `--vmwarefusion-configdrive-url` option for configuration via `cloud-init`
|
||||
- `amazonec2`
|
||||
- `--amazonec2-use-private-address` option to use private networking
|
||||
- `virtualbox`
|
||||
- Enhancements around robustness of the created host-only network
|
||||
- Fix IPv6 network mask prefix parsing
|
||||
- `--virtualbox-no-share` option to disable the automatic home directory mount
|
||||
- `--virtualbox-hostonly-nictype` and `--virtualbox-hostonly-nicpromisc` for controlling settings around the created hostonly NIC
|
||||
|
||||
# 0.4.1 (2015-08)
|
||||
|
||||
- Fixes `upgrade` functionality on Debian based systems
|
||||
- Fixes `upgrade` functionality on Ubuntu based systems
|
||||
|
||||
# 0.4.0 (2015-08-11)
|
||||
|
||||
## Updates
|
||||
|
||||
- HTTP Proxy support for Docker Engine
|
||||
- RedHat distros now use Docker Yum repositories
|
||||
- Ability to set environment variables in the Docker Engine
|
||||
- Internal libmachine updates for stability
|
||||
|
||||
## Drivers
|
||||
|
||||
- Google:
|
||||
- Preemptible instances
|
||||
- Static IP support
|
||||
|
||||
## Fixes
|
||||
|
||||
- Swarm Discovery Flag is verified
|
||||
- Timeout added to `ls` command to prevent hangups
|
||||
- SSH command failure now reports information about error
|
||||
- Configuration migration updates
|
||||
|
||||
# 0.3.0 (2015-06-18)
|
||||
|
||||
## Features
|
||||
|
||||
- Engine option configuration (ability to configure all engine options)
|
||||
- Swarm option configuration (ability to configure all swarm options)
|
||||
- New Provisioning system to allow for greater flexibility and stability for installing and configuring Docker
|
||||
- New Provisioners
|
||||
- Rancher OS
|
||||
- RedHat Enterprise Linux 7.0+ (experimental)
|
||||
- Fedora 21+ (experimental)
|
||||
- Debian 8+ (experimental)
|
||||
- PowerShell support (configure Windows Docker CLI)
|
||||
- Command Prompt (cmd.exe) support (configure Windows Docker CLI)
|
||||
- Filter command help by driver
|
||||
- Ability to import Boot2Docker instances
|
||||
- Boot2Docker CLI migration guide (experimental)
|
||||
- Format option for `inspect` command
|
||||
- New logging output format to improve readability and display across platforms
|
||||
- Updated "active" machine concept - now is implicit according to `DOCKER_HOST` environment variable. Note: this removes the implicit "active" machine and can no longer be specified with the `active` command. You change the "active" host by using the `env` command instead.
|
||||
- Specify Swarm version (`--swarm-image` flag)
|
||||
|
||||
## Drivers
|
||||
|
||||
- New: Exoscale Driver
|
||||
- New: Generic Driver (provision any host with supported base OS and SSH)
|
||||
- Amazon EC2
|
||||
- SSH user is configurable
|
||||
- Support for Spot instances
|
||||
- Add option to use private address only
|
||||
- Base AMI updated to 20150417
|
||||
- Google
|
||||
- Support custom disk types
|
||||
- Updated base image to v20150316
|
||||
- Openstack
|
||||
- Support for Keystone v3 domains
|
||||
- Rackspace
|
||||
- Misc fixes including environment variable for Flavor Id and stability
|
||||
- Softlayer
|
||||
- Enable local disk as provisioning option
|
||||
- Fixes for SSH access errors
|
||||
- Fixed bug where public IP would always be returned when requesting private
|
||||
- Add support for specifying public and private VLAN IDs
|
||||
- VirtualBox
|
||||
- Use Intel network interface driver (adds great stability)
|
||||
- Stability fixes for NAT access
|
||||
- Use DNS pass through
|
||||
- Default CPU to single core for improved performance
|
||||
- Enable shared folder support for Windows hosts
|
||||
- VMware Fusion
|
||||
- Boot2Docker ISO updated
|
||||
- Shared folder support
|
||||
|
||||
## Fixes
|
||||
|
||||
- Provisioning improvements to ensure Docker is available
|
||||
- SSH improvements for provisioning stability
|
||||
- Fixed SSH key generation bug on Windows
|
||||
- Help formatting for improved readability
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
- "Short-Form" name reference no longer supported Instead of "docker-machine " implying the active host you must now use docker-machine
|
||||
- VMware shared folders require Boot2Docker 1.7
|
||||
|
||||
## Special Thanks
|
||||
|
||||
We would like to thank all contributors. Machine would not be where it is
|
||||
without you. We would also like to give special thanks to the following
|
||||
contributors for outstanding contributions to the project:
|
||||
|
||||
- @frapposelli for VMware updates and fixes
|
||||
- @hairyhenderson for several improvements to Softlayer driver, inspect formatting and lots of fixes
|
||||
- @ibuildthecloud for rancher os provisioning
|
||||
- @sthulb for portable SSH library
|
||||
- @vincentbernat for exoscale
|
||||
- @zchee for Amazon updates and great doc updates
|
||||
|
||||
# 0.2.0 (2015-04-16)
|
||||
|
||||
Core Stability and Driver Updates
|
||||
|
||||
## Core
|
||||
|
||||
- Support for system proxy environment
|
||||
- New command to regenerate TLS certificates
|
||||
- Note: this will restart the Docker engine to apply
|
||||
- Updates to driver operations (create, start, stop, etc) for better reliability
|
||||
- New internal `libmachine` package for internal api (not ready for public usage)
|
||||
- Updated Driver Interface
|
||||
- [Driver Spec](https://github.com/docker/machine/blob/master/docs/DRIVER_SPEC.md)
|
||||
- Removed host provisioning from Drivers to enable a more consistent install
|
||||
- Removed SSH commands from each Driver for more consistent operations
|
||||
- Swarm: machine now uses Swarm default binpacking strategy
|
||||
|
||||
## Driver Updates
|
||||
|
||||
- All drivers updated to new Driver interface
|
||||
- Amazon EC2
|
||||
- Better checking for subnets on creation
|
||||
- Support for using Private IPs in VPC
|
||||
- Fixed bug with duplicate security group authorization with Swarm
|
||||
- Support for IAM instance profile
|
||||
- Fixed bug where IP was not properly detected upon stop
|
||||
- DigitalOcean
|
||||
- IPv6 support
|
||||
- Backup option
|
||||
- Private Networking
|
||||
- Openstack / Rackspace
|
||||
- Gophercloud updated to latest version
|
||||
- New insecure flag to disable TLS (use with caution)
|
||||
- Google
|
||||
- Google source image updated
|
||||
- Ability to specify auth token via file
|
||||
- VMware Fusion
|
||||
- Paravirtualized driver for disk (pvscsi)
|
||||
- Enhanced paravirtualized NIC (vmxnet3)
|
||||
- Power option updates
|
||||
- SSH keys persistent across reboots
|
||||
- Stop now gracefully stops VM
|
||||
- vCPUs now match host CPUs
|
||||
- SoftLayer
|
||||
- Fixed provision bug where `curl` was not present
|
||||
- VirtualBox
|
||||
- Correct power operations with Saved VM state
|
||||
- Fixed bug where image option was ignored
|
||||
|
||||
## CLI
|
||||
|
||||
- Auto-regeneration of TLS certificates when TLS error is detected
|
||||
- Note: this will restart the Docker engine to apply
|
||||
- Minor UI updates including improved sorting and updated command docs
|
||||
- Bug with `config` and `env` with spaces fixed
|
||||
- Note: you now must use `eval $(docker-machine env machine)` to load environment settings
|
||||
- Updates to better support `fish` shell
|
||||
- Use `--tlsverify` for both `config` and `env` commands
|
||||
- Commands now use eval for better interoperability with shell
|
||||
|
||||
## Testing
|
||||
|
||||
- New integration test framework (bats)
|
||||
|
||||
# 0.1.0 (2015-02-26)
|
||||
|
||||
Initial beta release.
|
||||
|
||||
- Provision Docker Engines using multiple drivers
|
||||
- Provide light management for the machines
|
||||
- Create, Start, Stop, Restart, Kill, Remove, SSH
|
||||
- Configure the Docker Engine for secure communication (TLS)
|
||||
- Easily switch target machine for fast configuration of Docker Engine client
|
||||
- Provision Swarm clusters (experimental)
|
||||
|
||||
## Included drivers
|
||||
|
||||
- Amazon EC2
|
||||
- Digital Ocean
|
||||
- Google
|
||||
- Microsoft Azure
|
||||
- Microsoft Hyper-V
|
||||
- Openstack
|
||||
- Rackspace
|
||||
- VirtualBox
|
||||
- VMware Fusion
|
||||
- VMware vCloud Air
|
||||
- VMware vSphere
|
||||
|
|
@ -0,0 +1,288 @@
|
|||
# Contributing to machine
|
||||
|
||||
[](https://godoc.org/github.com/docker/machine)
|
||||
[](https://travis-ci.org/docker/machine)
|
||||
[](https://ci.appveyor.com/project/dmp42/machine-fp5u5)
|
||||
[](https://coveralls.io/github/docker/machine?branch=master)
|
||||
|
||||
Want to hack on Machine? Awesome! Here are instructions to get you
|
||||
started.
|
||||
|
||||
Machine is a part of the [Docker](https://www.docker.com) project, and follows
|
||||
the same rules and principles. If you're already familiar with the way
|
||||
Docker does things, you'll feel right at home.
|
||||
|
||||
Otherwise, please read [Docker's contributions
|
||||
guidelines](https://github.com/docker/docker/blob/master/CONTRIBUTING.md).
|
||||
|
||||
# Building
|
||||
|
||||
The requirements to build Machine are:
|
||||
|
||||
1. A running instance of Docker or a Golang 1.6 development environment
|
||||
2. The `bash` shell
|
||||
3. [Make](https://www.gnu.org/software/make/)
|
||||
|
||||
## Build using Docker containers
|
||||
|
||||
To build the `docker-machine` binary using containers, simply run:
|
||||
|
||||
$ export USE_CONTAINER=true
|
||||
$ make build
|
||||
|
||||
## Local Go development environment
|
||||
|
||||
Make sure the source code directory is under a correct directory structure;
|
||||
Example of cloning and preparing the correct environment `GOPATH`:
|
||||
|
||||
$ mkdir docker-machine
|
||||
$ cd docker-machine
|
||||
$ export GOPATH="$PWD"
|
||||
$ go get github.com/docker/machine
|
||||
$ cd src/github.com/docker/machine
|
||||
|
||||
If you want to use your existing workspace, make sure your `GOPATH` is set to
|
||||
the directory that contains your `src` directory, e.g.:
|
||||
|
||||
$ export GOPATH=/home/yourname/work
|
||||
$ mkdir -p $GOPATH/src/github.com/docker
|
||||
$ cd $GOPATH/src/github.com/docker && git clone git@github.com:docker/machine.git
|
||||
$ cd machine
|
||||
|
||||
At this point, simply run:
|
||||
|
||||
$ make build
|
||||
|
||||
## Built binary
|
||||
|
||||
After the build is complete a `bin/docker-machine` binary will be created.
|
||||
|
||||
You may call:
|
||||
|
||||
$ make clean
|
||||
|
||||
to clean-up build results.
|
||||
|
||||
## Tests and validation
|
||||
|
||||
We use the usual `go` tools for this, to run those commands you need at least the linter which you can
|
||||
install with `go get -u github.com/golang/lint/golint`
|
||||
|
||||
To run basic validation (dco, fmt), and the project unit tests, call:
|
||||
|
||||
$ make test
|
||||
|
||||
If you want more indepth validation (vet, lint), and all tests with race detection, call:
|
||||
|
||||
$ make validate
|
||||
|
||||
If you make a pull request, it is highly encouraged that you submit tests for
|
||||
the code that you have added or modified in the same pull request.
|
||||
|
||||
## Code Coverage
|
||||
|
||||
To generate an html code coverage report of the Machine codebase, run:
|
||||
|
||||
make coverage-serve
|
||||
|
||||
And navigate to <http://localhost:8000> (hit `CTRL+C` to stop the server).
|
||||
|
||||
### Native build
|
||||
|
||||
Alternatively, if you are building natively, you can simply run:
|
||||
|
||||
make coverage-html
|
||||
|
||||
This will generate and open the report file:
|
||||
|
||||

|
||||
|
||||
## List of all targets
|
||||
|
||||
### High-level targets
|
||||
|
||||
make clean
|
||||
make build
|
||||
make test
|
||||
make validate
|
||||
|
||||
### Advanced build targets
|
||||
|
||||
Build for all supported OSes and architectures (binaries will be in the `bin` project subfolder):
|
||||
|
||||
make build-x
|
||||
|
||||
Build for a specific list of OSes and architectures:
|
||||
|
||||
TARGET_OS=linux TARGET_ARCH="amd64 arm" make build-x
|
||||
|
||||
You can further control build options through the following environment variables:
|
||||
|
||||
DEBUG=true # enable debug build
|
||||
STATIC=true # build static (note: when cross-compiling, the build is always static)
|
||||
VERBOSE=true # verbose output
|
||||
PREFIX=folder # put binaries in another folder (not the default `./bin`)
|
||||
|
||||
Scrub build results:
|
||||
|
||||
make build-clean
|
||||
|
||||
### Coverage targets
|
||||
|
||||
make coverage-html
|
||||
make coverage-serve
|
||||
make coverage-send
|
||||
make coverage-generate
|
||||
make coverage-clean
|
||||
|
||||
### Tests targets
|
||||
|
||||
make test-short
|
||||
make test-long
|
||||
make test-integration
|
||||
|
||||
### Validation targets
|
||||
|
||||
make fmt
|
||||
make vet
|
||||
make lint
|
||||
make dco
|
||||
|
||||
### Restore, update and save dependencies
|
||||
|
||||
When you make a fresh copy of the repo, all the dependecies are in `vendor/` directory for the builds to work. If you want to update the dependencies
|
||||
|
||||
#### Restore the dependencies
|
||||
|
||||
make dep-restore
|
||||
|
||||
This uses godep to restores all the dependencies to your `$GOPATH`. Note that this changes the packages in your `$GOPATH`
|
||||
|
||||
#### Add one ore more dependencies
|
||||
|
||||
go get -u <new dependency>
|
||||
|
||||
#### Save the dependencies to `vendor/`
|
||||
|
||||
make dep-save
|
||||
|
||||
4. Verify the changes in your repo, commit and submit a pull request
|
||||
|
||||
## Integration Tests
|
||||
|
||||
### Setup
|
||||
|
||||
We use [BATS](https://github.com/sstephenson/bats) for integration testing, so,
|
||||
first make sure to [install it](https://github.com/sstephenson/bats#installing-bats-from-source).
|
||||
|
||||
### Basic Usage
|
||||
|
||||
You first need to build, calling `make build`.
|
||||
|
||||
You can then invoke integration tests calling `DRIVER=foo make test-integration TESTSUITE`, where `TESTSUITE` is
|
||||
one of the `test/integration` subfolder, and `foo` is the specific driver you want to test.
|
||||
|
||||
Examples:
|
||||
|
||||
```console
|
||||
$ DRIVER=virtualbox make test-integration test/integration/core/core-commands.bats
|
||||
✓ virtualbox: machine should not exist
|
||||
✓ virtualbox: create
|
||||
✓ virtualbox: ls
|
||||
✓ virtualbox: run busybox container
|
||||
✓ virtualbox: url
|
||||
✓ virtualbox: ip
|
||||
✓ virtualbox: ssh
|
||||
✓ virtualbox: docker commands with the socket should work
|
||||
✓ virtualbox: stop
|
||||
✓ virtualbox: machine should show stopped after stop
|
||||
✓ virtualbox: machine should now allow upgrade when stopped
|
||||
✓ virtualbox: start
|
||||
✓ virtualbox: machine should show running after start
|
||||
✓ virtualbox: kill
|
||||
✓ virtualbox: machine should show stopped after kill
|
||||
✓ virtualbox: restart
|
||||
✓ virtualbox: machine should show running after restart
|
||||
|
||||
17 tests, 0 failures
|
||||
Cleaning up machines...
|
||||
Successfully removed bats-virtualbox-test
|
||||
```
|
||||
|
||||
To invoke a directory of tests recursively:
|
||||
|
||||
```console
|
||||
$ DRIVER=virtualbox make test-integration test/integration/core/
|
||||
...
|
||||
```
|
||||
|
||||
### Extra Create Arguments
|
||||
|
||||
In some cases, for instance to test the creation of a specific base OS (e.g.
|
||||
RHEL) as opposed to the default with the common tests, you may want to run
|
||||
common tests with different create arguments than you get out of the box.
|
||||
|
||||
Keep in mind that Machine supports environment variables for many of these
|
||||
flags. So, for instance, you could run the command (substituting, of course,
|
||||
the proper secrets):
|
||||
|
||||
$ DRIVER=amazonec2 \
|
||||
AWS_VPC_ID=vpc-xxxxxxx \
|
||||
AWS_SECRET_ACCESS_KEY=yyyyyyyyyyyyy \
|
||||
AWS_ACCESS_KEY_ID=zzzzzzzzzzzzzzzz \
|
||||
AWS_AMI=ami-12663b7a \
|
||||
AWS_SSH_USER=ec2-user \
|
||||
make test-integration test/integration/core
|
||||
|
||||
in order to run the core tests on Red Hat Enterprise Linux on Amazon.
|
||||
|
||||
### Layout
|
||||
|
||||
The `test/integration` directory is layed out to divide up tests based on the
|
||||
areas which the test. If you are uncertain where to put yours, we are happy to
|
||||
guide you.
|
||||
|
||||
At the time of writing, there is:
|
||||
|
||||
1. A `core` directory which contains tests that are applicable to all drivers.
|
||||
2. A `drivers` directory which contains tests that are applicable only to
|
||||
specific drivers with sub-directories for each provider.
|
||||
3. A `cli` directory which is meant for testing functionality of the command
|
||||
line interface, without much regard for driver-specific details.
|
||||
|
||||
### Guidelines
|
||||
|
||||
The best practices for writing integration tests on Docker Machine are still a
|
||||
work in progress, but here are some general guidelines from the maintainers:
|
||||
|
||||
1. Ideally, each test file should have only one concern.
|
||||
2. Tests generally should not spin up more than one machine unless the test is
|
||||
deliberately testing something which involves multiple machines, such as an `ls`
|
||||
test which involves several machines, or a test intended to create and check
|
||||
some property of a Swarm cluster.
|
||||
3. BATS will print the output of commands executed during a test if the test
|
||||
fails. This can be useful, for instance to dump the magic `$output` variable
|
||||
that BATS provides and/or to get debugging information.
|
||||
4. It is not strictly needed to clean up the machines as part of the test. The
|
||||
BATS wrapper script has a hook to take care of cleaning up all created machines
|
||||
after each test.
|
||||
|
||||
# Drivers
|
||||
|
||||
Docker Machine has several included drivers that supports provisioning hosts
|
||||
in various providers. If you wish to contribute a driver, we ask the following
|
||||
to ensure we keep the driver in a consistent and stable state:
|
||||
|
||||
- Address issues filed against this driver in a timely manner
|
||||
- Review PRs for the driver
|
||||
- Be responsible for maintaining the infrastructure to run unit tests
|
||||
and integration tests on the new supported environment
|
||||
- Participate in a weekly driver maintainer meeting
|
||||
|
||||
If you can commit to those, the next step is to make sure the driver adheres
|
||||
to the [spec](https://github.com/docker/machine/blob/master/docs/DRIVER_SPEC.md).
|
||||
|
||||
Once you have created and tested the driver, you can open a PR.
|
||||
|
||||
Note: even if those are met does not guarantee a driver will be accepted.
|
||||
If you have questions, please do not hesitate to contact us on IRC.
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
FROM golang:1.7.1
|
||||
|
||||
RUN go get github.com/golang/lint/golint \
|
||||
github.com/mattn/goveralls \
|
||||
golang.org/x/tools/cover
|
||||
|
||||
ENV USER root
|
||||
WORKDIR /go/src/github.com/docker/machine
|
||||
|
||||
COPY . ./
|
||||
RUN mkdir bin
|
||||
|
|
@ -0,0 +1,617 @@
|
|||
{
|
||||
"ImportPath": "github.com/docker/machine",
|
||||
"GoVersion": "go1.6",
|
||||
"GodepVersion": "v66",
|
||||
"Packages": [
|
||||
"github.com/docker/machine",
|
||||
"github.com/docker/machine/cmd",
|
||||
"github.com/docker/machine/commands",
|
||||
"github.com/docker/machine/commands/commandstest",
|
||||
"github.com/docker/machine/commands/mcndirs",
|
||||
"github.com/docker/machine/drivers/amazonec2",
|
||||
"github.com/docker/machine/drivers/azure",
|
||||
"github.com/docker/machine/drivers/azure/azureutil",
|
||||
"github.com/docker/machine/drivers/azure/logutil",
|
||||
"github.com/docker/machine/drivers/digitalocean",
|
||||
"github.com/docker/machine/drivers/errdriver",
|
||||
"github.com/docker/machine/drivers/exoscale",
|
||||
"github.com/docker/machine/drivers/fakedriver",
|
||||
"github.com/docker/machine/drivers/generic",
|
||||
"github.com/docker/machine/drivers/google",
|
||||
"github.com/docker/machine/drivers/hyperv",
|
||||
"github.com/docker/machine/drivers/none",
|
||||
"github.com/docker/machine/drivers/openstack",
|
||||
"github.com/docker/machine/drivers/rackspace",
|
||||
"github.com/docker/machine/drivers/softlayer",
|
||||
"github.com/docker/machine/drivers/virtualbox",
|
||||
"github.com/docker/machine/drivers/vmwarefusion",
|
||||
"github.com/docker/machine/drivers/vmwarevcloudair",
|
||||
"github.com/docker/machine/drivers/vmwarevsphere",
|
||||
"github.com/docker/machine/its",
|
||||
"github.com/docker/machine/its/cli",
|
||||
"github.com/docker/machine/its/thirdparty",
|
||||
"github.com/docker/machine/libmachine",
|
||||
"github.com/docker/machine/libmachine/auth",
|
||||
"github.com/docker/machine/libmachine/cert",
|
||||
"github.com/docker/machine/libmachine/check",
|
||||
"github.com/docker/machine/libmachine/crashreport",
|
||||
"github.com/docker/machine/libmachine/drivers",
|
||||
"github.com/docker/machine/libmachine/drivers/plugin",
|
||||
"github.com/docker/machine/libmachine/drivers/plugin/localbinary",
|
||||
"github.com/docker/machine/libmachine/drivers/rpc",
|
||||
"github.com/docker/machine/libmachine/engine",
|
||||
"github.com/docker/machine/libmachine/examples",
|
||||
"github.com/docker/machine/libmachine/host",
|
||||
"github.com/docker/machine/libmachine/hosttest",
|
||||
"github.com/docker/machine/libmachine/libmachinetest",
|
||||
"github.com/docker/machine/libmachine/log",
|
||||
"github.com/docker/machine/libmachine/mcndockerclient",
|
||||
"github.com/docker/machine/libmachine/mcnerror",
|
||||
"github.com/docker/machine/libmachine/mcnflag",
|
||||
"github.com/docker/machine/libmachine/mcnutils",
|
||||
"github.com/docker/machine/libmachine/persist",
|
||||
"github.com/docker/machine/libmachine/persist/persisttest",
|
||||
"github.com/docker/machine/libmachine/provider",
|
||||
"github.com/docker/machine/libmachine/provision",
|
||||
"github.com/docker/machine/libmachine/provision/pkgaction",
|
||||
"github.com/docker/machine/libmachine/provision/provisiontest",
|
||||
"github.com/docker/machine/libmachine/provision/serviceaction",
|
||||
"github.com/docker/machine/libmachine/shell",
|
||||
"github.com/docker/machine/libmachine/ssh",
|
||||
"github.com/docker/machine/libmachine/ssh/sshtest",
|
||||
"github.com/docker/machine/libmachine/state",
|
||||
"github.com/docker/machine/libmachine/swarm",
|
||||
"github.com/docker/machine/libmachine/version",
|
||||
"github.com/docker/machine/version"
|
||||
],
|
||||
"Deps": [
|
||||
{
|
||||
"ImportPath": "github.com/Azure/azure-sdk-for-go/arm/compute",
|
||||
"Comment": "v2.1.1-beta",
|
||||
"Rev": "a1883f7b98346e4908a6c25230c95a8a3026a10c"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/Azure/azure-sdk-for-go/arm/network",
|
||||
"Comment": "v2.1.1-beta",
|
||||
"Rev": "a1883f7b98346e4908a6c25230c95a8a3026a10c"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/Azure/azure-sdk-for-go/arm/resources/resources",
|
||||
"Comment": "v2.1.1-beta",
|
||||
"Rev": "a1883f7b98346e4908a6c25230c95a8a3026a10c"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/Azure/azure-sdk-for-go/arm/resources/subscriptions",
|
||||
"Comment": "v2.1.1-beta",
|
||||
"Rev": "a1883f7b98346e4908a6c25230c95a8a3026a10c"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/Azure/azure-sdk-for-go/arm/storage",
|
||||
"Comment": "v2.1.1-beta",
|
||||
"Rev": "a1883f7b98346e4908a6c25230c95a8a3026a10c"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/Azure/azure-sdk-for-go/storage",
|
||||
"Comment": "v2.1.1-beta",
|
||||
"Rev": "a1883f7b98346e4908a6c25230c95a8a3026a10c"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/Azure/go-autorest/autorest",
|
||||
"Comment": "v7.0.4-6-gec603a8",
|
||||
"Rev": "ec603a8ea5ffc45df35c7d948d9acfd6cbc07b46"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/Azure/go-autorest/autorest/azure",
|
||||
"Comment": "v7.0.4-6-gec603a8",
|
||||
"Rev": "ec603a8ea5ffc45df35c7d948d9acfd6cbc07b46"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/Azure/go-autorest/autorest/date",
|
||||
"Comment": "v7.0.4-6-gec603a8",
|
||||
"Rev": "ec603a8ea5ffc45df35c7d948d9acfd6cbc07b46"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/Azure/go-autorest/autorest/to",
|
||||
"Comment": "v7.0.4-6-gec603a8",
|
||||
"Rev": "ec603a8ea5ffc45df35c7d948d9acfd6cbc07b46"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/aws",
|
||||
"Comment": "v1.0.2",
|
||||
"Rev": "9d7bc2d6ca2ada0468f705f0e9725ca97f8550c8"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/aws/awserr",
|
||||
"Comment": "v1.0.2",
|
||||
"Rev": "9d7bc2d6ca2ada0468f705f0e9725ca97f8550c8"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/aws/awsutil",
|
||||
"Comment": "v1.0.2",
|
||||
"Rev": "9d7bc2d6ca2ada0468f705f0e9725ca97f8550c8"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/aws/client",
|
||||
"Comment": "v1.0.2",
|
||||
"Rev": "9d7bc2d6ca2ada0468f705f0e9725ca97f8550c8"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/aws/client/metadata",
|
||||
"Comment": "v1.0.2",
|
||||
"Rev": "9d7bc2d6ca2ada0468f705f0e9725ca97f8550c8"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/aws/corehandlers",
|
||||
"Comment": "v1.0.2",
|
||||
"Rev": "9d7bc2d6ca2ada0468f705f0e9725ca97f8550c8"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/aws/credentials",
|
||||
"Comment": "v1.0.2",
|
||||
"Rev": "9d7bc2d6ca2ada0468f705f0e9725ca97f8550c8"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds",
|
||||
"Comment": "v1.0.2",
|
||||
"Rev": "9d7bc2d6ca2ada0468f705f0e9725ca97f8550c8"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/aws/defaults",
|
||||
"Comment": "v1.0.2",
|
||||
"Rev": "9d7bc2d6ca2ada0468f705f0e9725ca97f8550c8"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/aws/ec2metadata",
|
||||
"Comment": "v1.0.2",
|
||||
"Rev": "9d7bc2d6ca2ada0468f705f0e9725ca97f8550c8"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/aws/request",
|
||||
"Comment": "v1.0.2",
|
||||
"Rev": "9d7bc2d6ca2ada0468f705f0e9725ca97f8550c8"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/aws/session",
|
||||
"Comment": "v1.0.2",
|
||||
"Rev": "9d7bc2d6ca2ada0468f705f0e9725ca97f8550c8"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/private/endpoints",
|
||||
"Comment": "v1.0.2",
|
||||
"Rev": "9d7bc2d6ca2ada0468f705f0e9725ca97f8550c8"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/ec2query",
|
||||
"Comment": "v1.0.2",
|
||||
"Rev": "9d7bc2d6ca2ada0468f705f0e9725ca97f8550c8"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/query/queryutil",
|
||||
"Comment": "v1.0.2",
|
||||
"Rev": "9d7bc2d6ca2ada0468f705f0e9725ca97f8550c8"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/rest",
|
||||
"Comment": "v1.0.2",
|
||||
"Rev": "9d7bc2d6ca2ada0468f705f0e9725ca97f8550c8"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil",
|
||||
"Comment": "v1.0.2",
|
||||
"Rev": "9d7bc2d6ca2ada0468f705f0e9725ca97f8550c8"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/private/signer/v4",
|
||||
"Comment": "v1.0.2",
|
||||
"Rev": "9d7bc2d6ca2ada0468f705f0e9725ca97f8550c8"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/private/waiter",
|
||||
"Comment": "v1.0.2",
|
||||
"Rev": "9d7bc2d6ca2ada0468f705f0e9725ca97f8550c8"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/service/ec2",
|
||||
"Comment": "v1.0.2",
|
||||
"Rev": "9d7bc2d6ca2ada0468f705f0e9725ca97f8550c8"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/bugsnag/bugsnag-go",
|
||||
"Comment": "v1.0.5-19-g02e9528",
|
||||
"Rev": "02e952891c52fbcb15f113d90633897355783b6e"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/bugsnag/bugsnag-go/errors",
|
||||
"Comment": "v1.0.5-19-g02e9528",
|
||||
"Rev": "02e952891c52fbcb15f113d90633897355783b6e"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/bugsnag/osext",
|
||||
"Rev": "0dd3f918b21bec95ace9dc86c7e70266cfc5c702"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/bugsnag/panicwrap",
|
||||
"Comment": "1.0.0",
|
||||
"Rev": "e5f9854865b9778a45169fc249e99e338d4d6f27"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/cenkalti/backoff",
|
||||
"Rev": "9831e1e25c874e0a0601b6dc43641071414eec7a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/codegangsta/cli",
|
||||
"Comment": "v1.11.0-6-g0302d39",
|
||||
"Rev": "0302d3914d2a6ad61404584cdae6e6dbc9c03599"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/davecgh/go-spew/spew",
|
||||
"Rev": "5215b55f46b2b919f50a1df0eaa5886afe4e3b3d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/dgrijalva/jwt-go",
|
||||
"Comment": "v2.4.0-4-gafef698",
|
||||
"Rev": "afef698c326bfd906b11659432544e5aae441d44"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/digitalocean/godo",
|
||||
"Comment": "v0.9.0-8-g2124bf3",
|
||||
"Rev": "2124bf3eeeb4ac070337bb19ef7b76a745de56f4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/docker/docker/pkg/term",
|
||||
"Comment": "v1.5.0",
|
||||
"Rev": "a8a31eff10544860d2188dddabdee4d727545796"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/docker/go-units",
|
||||
"Comment": "v0.1.0-21-g0bbddae",
|
||||
"Rev": "0bbddae09c5a5419a8c6dcdd7ff90da3d450393b"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/go-ini/ini",
|
||||
"Comment": "v0-56-g03e0e7d",
|
||||
"Rev": "03e0e7d51a13a91c765d8d0161246bc14a38001a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/golang/protobuf/proto",
|
||||
"Rev": "3c84672111d91bb5ac31719e112f9f7126a0e26e"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/google/go-querystring/query",
|
||||
"Rev": "30f7a39f4a218feb5325f3aebc60c32a572a8274"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/jmespath/go-jmespath",
|
||||
"Comment": "0.2.2",
|
||||
"Rev": "3433f3ea46d9f8019119e7dd41274e112a2359a9"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/mitchellh/mapstructure",
|
||||
"Rev": "740c764bc6149d3f1806231418adb9f52c11bcbf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/pmezard/go-difflib/difflib",
|
||||
"Rev": "792786c7400a136282c1664665ae0a8db921c6c2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/pyr/egoscale/src/egoscale",
|
||||
"Rev": "347f81398d2ea1f3eebf1cd27ee3183669e34819"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/rackspace/gophercloud",
|
||||
"Comment": "v1.0.0-558-gce0f487",
|
||||
"Rev": "ce0f487f6747ab43c4e4404722df25349385bebd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/rackspace/gophercloud/openstack",
|
||||
"Comment": "v1.0.0-558-gce0f487",
|
||||
"Rev": "ce0f487f6747ab43c4e4404722df25349385bebd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/floatingip",
|
||||
"Comment": "v1.0.0-558-gce0f487",
|
||||
"Rev": "ce0f487f6747ab43c4e4404722df25349385bebd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs",
|
||||
"Comment": "v1.0.0-558-gce0f487",
|
||||
"Rev": "ce0f487f6747ab43c4e4404722df25349385bebd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/startstop",
|
||||
"Comment": "v1.0.0-558-gce0f487",
|
||||
"Rev": "ce0f487f6747ab43c4e4404722df25349385bebd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/rackspace/gophercloud/openstack/compute/v2/flavors",
|
||||
"Comment": "v1.0.0-558-gce0f487",
|
||||
"Rev": "ce0f487f6747ab43c4e4404722df25349385bebd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/rackspace/gophercloud/openstack/compute/v2/images",
|
||||
"Comment": "v1.0.0-558-gce0f487",
|
||||
"Rev": "ce0f487f6747ab43c4e4404722df25349385bebd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/rackspace/gophercloud/openstack/compute/v2/servers",
|
||||
"Comment": "v1.0.0-558-gce0f487",
|
||||
"Rev": "ce0f487f6747ab43c4e4404722df25349385bebd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/rackspace/gophercloud/openstack/identity/v2/tenants",
|
||||
"Comment": "v1.0.0-558-gce0f487",
|
||||
"Rev": "ce0f487f6747ab43c4e4404722df25349385bebd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/rackspace/gophercloud/openstack/identity/v2/tokens",
|
||||
"Comment": "v1.0.0-558-gce0f487",
|
||||
"Rev": "ce0f487f6747ab43c4e4404722df25349385bebd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/rackspace/gophercloud/openstack/identity/v3/tokens",
|
||||
"Comment": "v1.0.0-558-gce0f487",
|
||||
"Rev": "ce0f487f6747ab43c4e4404722df25349385bebd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/floatingips",
|
||||
"Comment": "v1.0.0-558-gce0f487",
|
||||
"Rev": "ce0f487f6747ab43c4e4404722df25349385bebd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/rackspace/gophercloud/openstack/networking/v2/networks",
|
||||
"Comment": "v1.0.0-558-gce0f487",
|
||||
"Rev": "ce0f487f6747ab43c4e4404722df25349385bebd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/rackspace/gophercloud/openstack/networking/v2/ports",
|
||||
"Comment": "v1.0.0-558-gce0f487",
|
||||
"Rev": "ce0f487f6747ab43c4e4404722df25349385bebd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/rackspace/gophercloud/openstack/utils",
|
||||
"Comment": "v1.0.0-558-gce0f487",
|
||||
"Rev": "ce0f487f6747ab43c4e4404722df25349385bebd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/rackspace/gophercloud/pagination",
|
||||
"Comment": "v1.0.0-558-gce0f487",
|
||||
"Rev": "ce0f487f6747ab43c4e4404722df25349385bebd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/rackspace/gophercloud/rackspace",
|
||||
"Comment": "v1.0.0-558-gce0f487",
|
||||
"Rev": "ce0f487f6747ab43c4e4404722df25349385bebd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/rackspace/gophercloud/rackspace/identity/v2/tokens",
|
||||
"Comment": "v1.0.0-558-gce0f487",
|
||||
"Rev": "ce0f487f6747ab43c4e4404722df25349385bebd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/rackspace/gophercloud/testhelper",
|
||||
"Comment": "v1.0.0-558-gce0f487",
|
||||
"Rev": "ce0f487f6747ab43c4e4404722df25349385bebd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/rackspace/gophercloud/testhelper/client",
|
||||
"Comment": "v1.0.0-558-gce0f487",
|
||||
"Rev": "ce0f487f6747ab43c4e4404722df25349385bebd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/samalba/dockerclient",
|
||||
"Rev": "f661dd4754aa5c52da85d04b5871ee0e11f4b59c"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/skarademir/naturalsort",
|
||||
"Rev": "69a5d87bef620f77ee8508db30c846b3b84b111e"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/stretchr/objx",
|
||||
"Rev": "1a9d0bb9f541897e62256577b352fdbc1fb4fd94"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/stretchr/testify/assert",
|
||||
"Comment": "v1.1.3-4-g1f4a164",
|
||||
"Rev": "1f4a1643a57e798696635ea4c126e9127adb7d3c"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/stretchr/testify/mock",
|
||||
"Comment": "v1.1.3-4-g1f4a164",
|
||||
"Rev": "1f4a1643a57e798696635ea4c126e9127adb7d3c"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/tent/http-link-go",
|
||||
"Rev": "ac974c61c2f990f4115b119354b5e0b47550e888"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/vmware/govcloudair",
|
||||
"Comment": "v0.0.2",
|
||||
"Rev": "66a23eaabc61518f91769939ff541886fe1dceef"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/vmware/govcloudair/types/v56",
|
||||
"Comment": "v0.0.2",
|
||||
"Rev": "66a23eaabc61518f91769939ff541886fe1dceef"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/vmware/govmomi",
|
||||
"Comment": "v0.1.0-454-g9051bd6",
|
||||
"Rev": "9051bd6b44125d9472e0c148b5965692ab283d4a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/vmware/govmomi/find",
|
||||
"Comment": "v0.1.0-454-g9051bd6",
|
||||
"Rev": "9051bd6b44125d9472e0c148b5965692ab283d4a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/vmware/govmomi/guest",
|
||||
"Comment": "v0.1.0-454-g9051bd6",
|
||||
"Rev": "9051bd6b44125d9472e0c148b5965692ab283d4a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/vmware/govmomi/list",
|
||||
"Comment": "v0.1.0-454-g9051bd6",
|
||||
"Rev": "9051bd6b44125d9472e0c148b5965692ab283d4a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/vmware/govmomi/object",
|
||||
"Comment": "v0.1.0-454-g9051bd6",
|
||||
"Rev": "9051bd6b44125d9472e0c148b5965692ab283d4a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/vmware/govmomi/property",
|
||||
"Comment": "v0.1.0-454-g9051bd6",
|
||||
"Rev": "9051bd6b44125d9472e0c148b5965692ab283d4a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/vmware/govmomi/session",
|
||||
"Comment": "v0.1.0-454-g9051bd6",
|
||||
"Rev": "9051bd6b44125d9472e0c148b5965692ab283d4a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/vmware/govmomi/task",
|
||||
"Comment": "v0.1.0-454-g9051bd6",
|
||||
"Rev": "9051bd6b44125d9472e0c148b5965692ab283d4a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/vmware/govmomi/vim25",
|
||||
"Comment": "v0.1.0-454-g9051bd6",
|
||||
"Rev": "9051bd6b44125d9472e0c148b5965692ab283d4a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/vmware/govmomi/vim25/debug",
|
||||
"Comment": "v0.1.0-454-g9051bd6",
|
||||
"Rev": "9051bd6b44125d9472e0c148b5965692ab283d4a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/vmware/govmomi/vim25/methods",
|
||||
"Comment": "v0.1.0-454-g9051bd6",
|
||||
"Rev": "9051bd6b44125d9472e0c148b5965692ab283d4a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/vmware/govmomi/vim25/mo",
|
||||
"Comment": "v0.1.0-454-g9051bd6",
|
||||
"Rev": "9051bd6b44125d9472e0c148b5965692ab283d4a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/vmware/govmomi/vim25/progress",
|
||||
"Comment": "v0.1.0-454-g9051bd6",
|
||||
"Rev": "9051bd6b44125d9472e0c148b5965692ab283d4a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/vmware/govmomi/vim25/soap",
|
||||
"Comment": "v0.1.0-454-g9051bd6",
|
||||
"Rev": "9051bd6b44125d9472e0c148b5965692ab283d4a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/vmware/govmomi/vim25/types",
|
||||
"Comment": "v0.1.0-454-g9051bd6",
|
||||
"Rev": "9051bd6b44125d9472e0c148b5965692ab283d4a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/vmware/govmomi/vim25/xml",
|
||||
"Comment": "v0.1.0-454-g9051bd6",
|
||||
"Rev": "9051bd6b44125d9472e0c148b5965692ab283d4a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/crypto/curve25519",
|
||||
"Rev": "beef0f4390813b96e8e68fd78570396d0f4751fc"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/crypto/ssh",
|
||||
"Rev": "beef0f4390813b96e8e68fd78570396d0f4751fc"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/crypto/ssh/terminal",
|
||||
"Rev": "beef0f4390813b96e8e68fd78570396d0f4751fc"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/context",
|
||||
"Rev": "4f2fc6c1e69d41baf187332ee08fbd2b296f21ed"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/context/ctxhttp",
|
||||
"Rev": "4f2fc6c1e69d41baf187332ee08fbd2b296f21ed"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/oauth2",
|
||||
"Rev": "442624c9ec9243441e83b374a9e22ac549b5c51d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/oauth2/google",
|
||||
"Rev": "442624c9ec9243441e83b374a9e22ac549b5c51d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/oauth2/internal",
|
||||
"Rev": "442624c9ec9243441e83b374a9e22ac549b5c51d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/oauth2/jws",
|
||||
"Rev": "442624c9ec9243441e83b374a9e22ac549b5c51d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/oauth2/jwt",
|
||||
"Rev": "442624c9ec9243441e83b374a9e22ac549b5c51d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/sys/windows/registry",
|
||||
"Rev": "d9157a9621b69ad1d8d77a1933590c416593f24f"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/api/compute/v1",
|
||||
"Rev": "030d584ade5f79aa2ed0ce067e8f7da50c9a10d5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/api/gensupport",
|
||||
"Rev": "030d584ade5f79aa2ed0ce067e8f7da50c9a10d5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/api/googleapi",
|
||||
"Rev": "030d584ade5f79aa2ed0ce067e8f7da50c9a10d5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/api/googleapi/internal/uritemplates",
|
||||
"Rev": "030d584ade5f79aa2ed0ce067e8f7da50c9a10d5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/appengine",
|
||||
"Rev": "6a436539be38c296a8075a871cc536686b458371"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/appengine/internal",
|
||||
"Rev": "6a436539be38c296a8075a871cc536686b458371"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/appengine/internal/app_identity",
|
||||
"Rev": "6a436539be38c296a8075a871cc536686b458371"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/appengine/internal/base",
|
||||
"Rev": "6a436539be38c296a8075a871cc536686b458371"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/appengine/internal/datastore",
|
||||
"Rev": "6a436539be38c296a8075a871cc536686b458371"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/appengine/internal/log",
|
||||
"Rev": "6a436539be38c296a8075a871cc536686b458371"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/appengine/internal/modules",
|
||||
"Rev": "6a436539be38c296a8075a871cc536686b458371"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/appengine/internal/remote_api",
|
||||
"Rev": "6a436539be38c296a8075a871cc536686b458371"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/cloud/compute/metadata",
|
||||
"Rev": "975617b05ea8a58727e6c1a06b6161ff4185a9f2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/cloud/internal",
|
||||
"Rev": "975617b05ea8a58727e6c1a06b6161ff4185a9f2"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
This directory tree is generated automatically by godep.
|
||||
|
||||
Please do not edit.
|
||||
|
||||
See https://github.com/tools/godep for more information.
|
||||
|
|
@ -0,0 +1,191 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
Copyright 2014 Docker, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
# Machine maintainers file
|
||||
#
|
||||
# This file describes who runs the docker/machine project and how.
|
||||
# This is a living document - if you see something out of date or missing, speak up!
|
||||
#
|
||||
# It is structured to be consumable by both humans and programs.
|
||||
# To extract its contents programmatically, use any TOML-compliant parser.
|
||||
#
|
||||
# This file is compiled into the MAINTAINERS file in docker/opensource.
|
||||
#
|
||||
[Org]
|
||||
[Org."Core maintainers"]
|
||||
people = [
|
||||
"dgageot",
|
||||
"ehazlett",
|
||||
"jeanlaurent",
|
||||
"nathanleclaire",
|
||||
]
|
||||
|
||||
[people]
|
||||
|
||||
# A reference list of all people associated with the project.
|
||||
# All other sections should refer to people by their canonical key
|
||||
# in the people section.
|
||||
|
||||
# ADD YOURSELF HERE IN ALPHABETICAL ORDER
|
||||
|
||||
[people.dgageot]
|
||||
Name = "David Gageot"
|
||||
Email = "david.gageot@docker.com"
|
||||
GitHub = "dgageot"
|
||||
|
||||
[people.ehazlett]
|
||||
Name = "Evan Hazlett"
|
||||
Email = "ejhazlett@gmail.com"
|
||||
GitHub = "ehazlett"
|
||||
|
||||
[people.jeanlaurent]
|
||||
Name = "Jean-Laurent de Morlhon"
|
||||
Email = "jeanlaurent@docker.com>"
|
||||
GitHub = "jeanlaurent"
|
||||
|
||||
[people.nathanleclaire]
|
||||
Name = "Nathan LeClaire"
|
||||
Email = "nathan.leclaire@docker.com"
|
||||
GitHub = "nathanleclaire"
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
# Plain make targets if not requested inside a container
|
||||
ifneq (,$(findstring test-integration,$(MAKECMDGOALS)))
|
||||
include Makefile.inc
|
||||
include mk/main.mk
|
||||
else ifneq ($(USE_CONTAINER), true)
|
||||
include Makefile.inc
|
||||
include mk/main.mk
|
||||
else
|
||||
# Otherwise, with docker, swallow all targets and forward into a container
|
||||
DOCKER_BUILD_DONE := ""
|
||||
|
||||
test: .DEFAULT
|
||||
|
||||
.DEFAULT:
|
||||
@test ! -z "$(DOCKER_BUILD_DONE)" || ./script/build_in_container.sh $(MAKECMDGOALS)
|
||||
$(eval DOCKER_BUILD_DONE := "done")
|
||||
|
||||
endif
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
# Project name, used to name the binaries
|
||||
PKG_NAME := docker-machine
|
||||
|
||||
# If true, disable optimizations and does NOT strip the binary
|
||||
DEBUG ?=
|
||||
# If true, "build" will produce a static binary (cross compile always produce static build regardless)
|
||||
STATIC ?=
|
||||
# If true, turn on verbose output for build
|
||||
VERBOSE ?=
|
||||
# Build tags
|
||||
BUILDTAGS ?=
|
||||
# Adjust number of parallel builds (XXX not used)
|
||||
PARALLEL ?= -1
|
||||
# Coverage default directory
|
||||
COVERAGE_DIR ?= cover
|
||||
# Whether to perform targets inside a docker container, or natively on the host
|
||||
USE_CONTAINER ?=
|
||||
|
||||
# List of cross compilation targets
|
||||
ifeq ($(TARGET_OS),)
|
||||
TARGET_OS := darwin linux windows
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET_ARCH),)
|
||||
TARGET_ARCH := amd64 386
|
||||
endif
|
||||
|
||||
# Output prefix, defaults to local directory if not specified
|
||||
ifeq ($(PREFIX),)
|
||||
PREFIX := $(shell pwd)
|
||||
endif
|
||||
|
|
@ -0,0 +1,143 @@
|
|||
# Docker Machine
|
||||
|
||||

|
||||
|
||||
Machine lets you create Docker hosts on your computer, on cloud providers, and
|
||||
inside your own data center. It creates servers, installs Docker on them, then
|
||||
configures the Docker client to talk to them.
|
||||
|
||||
It works a bit like this:
|
||||
|
||||
```console
|
||||
$ docker-machine create -d virtualbox default
|
||||
Running pre-create checks...
|
||||
Creating machine...
|
||||
(default) Creating VirtualBox VM...
|
||||
(default) Creating SSH key...
|
||||
(default) Starting VM...
|
||||
Waiting for machine to be running, this may take a few minutes...
|
||||
Machine is running, waiting for SSH to be available...
|
||||
Detecting operating system of created instance...
|
||||
Detecting the provisioner...
|
||||
Provisioning with boot2docker...
|
||||
Copying certs to the local machine directory...
|
||||
Copying certs to the remote machine...
|
||||
Setting Docker configuration on the remote daemon...
|
||||
Checking connection to Docker...
|
||||
Docker is up and running!
|
||||
To see how to connect Docker to this machine, run: docker-machine env default
|
||||
|
||||
$ docker-machine ls
|
||||
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
|
||||
default - virtualbox Running tcp://192.168.99.188:2376 v1.9.1
|
||||
|
||||
$ eval "$(docker-machine env default)"
|
||||
|
||||
$ docker run busybox echo hello world
|
||||
Unable to find image 'busybox:latest' locally
|
||||
511136ea3c5a: Pull complete
|
||||
df7546f9f060: Pull complete
|
||||
ea13149945cb: Pull complete
|
||||
4986bf8c1536: Pull complete
|
||||
hello world
|
||||
```
|
||||
|
||||
In addition to local VMs, you can create and manage cloud servers:
|
||||
|
||||
```console
|
||||
$ docker-machine create -d digitalocean --digitalocean-access-token=secret staging
|
||||
Creating SSH key...
|
||||
Creating Digital Ocean droplet...
|
||||
To see how to connect Docker to this machine, run: docker-machine env staging
|
||||
|
||||
$ docker-machine ls
|
||||
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
|
||||
default - virtualbox Running tcp://192.168.99.188:2376 v1.9.1
|
||||
staging - digitalocean Running tcp://203.0.113.81:2376 v1.9.1
|
||||
```
|
||||
|
||||
## Installation and documentation
|
||||
|
||||
Full documentation [is available here](https://docs.docker.com/machine/).
|
||||
|
||||
## Contributing
|
||||
|
||||
Want to hack on Machine? Please start with the [Contributing Guide](https://github.com/docker/machine/blob/master/CONTRIBUTING.md).
|
||||
|
||||
## Driver Plugins
|
||||
|
||||
In addition to the core driver plugins bundled alongside Docker Machine, users
|
||||
can make and distribute their own plugin for any virtualization technology or
|
||||
cloud provider. To browse the list of known Docker Machine plugins, please [see
|
||||
this document in our
|
||||
repo](https://github.com/docker/machine/blob/master/docs/AVAILABLE_DRIVER_PLUGINS.md).
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
Docker Machine tries to do the right thing in a variety of scenarios but
|
||||
sometimes things do not go according to plan. Here is a quick troubleshooting
|
||||
guide which may help you to resolve of the issues you may be seeing.
|
||||
|
||||
Note that some of the suggested solutions are only available on the Docker
|
||||
Machine master branch. If you need them, consider compiling Docker Machine from
|
||||
source.
|
||||
#### `docker-machine` hangs
|
||||
|
||||
A common issue with Docker Machine is that it will hang when attempting to start
|
||||
up the virtual machine. Since starting the machine is part of the `create`
|
||||
process, `create` is often where these types of errors show up.
|
||||
|
||||
A hang could be due to a variety of factors, but the most common suspect is
|
||||
networking. Consider the following:
|
||||
|
||||
- Are you using a VPN? If so, try disconnecting and see if creation will
|
||||
succeed without the VPN. Some VPN software aggressively controls routes and
|
||||
you may need to [manually add the route](https://github.com/docker/machine/issues/1500#issuecomment-121134958).
|
||||
- Are you connected to a proxy server, corporate or otherwise? If so, take a
|
||||
look at the `--no-proxy` flag for `env` and at [setting environment variables
|
||||
for the created Docker Engine](https://docs.docker.com/machine/reference/create/#specifying-configuration-options-for-the-created-docker-engine).
|
||||
- Are there a lot of host-only interfaces listed by the command `VBoxManage list
|
||||
hostonlyifs`? If so, this has sometimes been known to cause bugs. Consider
|
||||
removing the ones you are not using (`VBoxManage hostonlyif remove name`) and
|
||||
trying machine creation again.
|
||||
|
||||
We are keenly aware of this as an issue and working towards a set of solutions
|
||||
which is robust for all users, so please give us feedback and/or report issues,
|
||||
workarounds, and desired workflows as you discover them.
|
||||
|
||||
#### Machine creation errors out before finishing
|
||||
|
||||
If you see messages such as "exit status 1" creating machines with VirtualBox,
|
||||
this frequently indicates that there is an issue with VirtualBox itself. Please
|
||||
[file an issue](https://github.com/docker/machine/issues/new) and include a link
|
||||
to a [Github Gist](https://gist.github.com/) with the output of the VirtualBox
|
||||
log (usually located at
|
||||
`$HOME/.docker/machine/machines/machinename/machinename/Logs/VBox.log`), as well
|
||||
as the output of running the Docker Machine command which is failing with the
|
||||
global `--debug` flag enabled. This will help us to track down which versions
|
||||
of VirtualBox are failing where, and under which conditions.
|
||||
|
||||
If you see messages such as "exit status 255", this frequently indicates there
|
||||
has been an issue with SSH. Please investigate your SSH configuration if you
|
||||
have one, and/or [file an issue](https://github.com/docker/machine/issues).
|
||||
|
||||
#### "You may be getting rate limited by Github" error message
|
||||
|
||||
In order to `create` or `upgrade` virtual machines running Docker, Docker
|
||||
Machine will check the Github API for the latest release of the [boot2docker
|
||||
operating system](https://github.com/boot2docker/boot2docker). The Github API
|
||||
allows for a small number of unauthenticated requests from a given client, but
|
||||
if you share an IP address with many other users (e.g. in an office), you may
|
||||
get rate limited by their API, and Docker Machine will error out with messages
|
||||
indicating this.
|
||||
|
||||
In order to work around this issue, you can [generate a
|
||||
token](https://help.github.com/articles/creating-an-access-token-for-command-line-use/)
|
||||
and pass it to Docker Machine using the global `--github-api-token` flag like
|
||||
so:
|
||||
|
||||
```console
|
||||
$ docker-machine --github-api-token=token create -d virtualbox newbox
|
||||
```
|
||||
|
||||
This should eliminate any issues you've been experiencing with rate limiting.
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
# Machine Roadmap
|
||||
|
||||
Machine currently works really well for development and test environments. The
|
||||
goal is to make it work better for provisioning and managing production
|
||||
environments.
|
||||
|
||||
This is not a simple task -- production is inherently far more complex than
|
||||
development -- but there are three things which are big steps towards that goal:
|
||||
**client/server architecture**, **swarm integration** and **flexible
|
||||
provisioning**.
|
||||
|
||||
(Note: this document is a high-level overview of where we are taking Machine.
|
||||
For what is coming in specific releases, see our [upcoming
|
||||
milestones](https://github.com/docker/machine/milestones).)
|
||||
|
||||
### Docker Engine / Swarm Configuration
|
||||
|
||||
Currently there are only a few things that can be configured in the Docker Engine and Swarm. This will enable more operations such as Engine labels and Swarm strategies.
|
||||
|
||||
### Boot2Docker Migration Support
|
||||
|
||||
Currently both Machine and Boot2Docker provider similar functionality. This will enable users to migrate from boot2docker to machine.
|
||||
|
||||
### Expand Provisioner
|
||||
|
||||
Machine currently supports running Boot2Docker for "local" providers and Ubuntu for "remote" providers. This will expand the provisioning capabilities to include other base operating systems such as Red Hat-like distributions and possibly other "just enough" operating systems.
|
||||
|
||||
### Windows Experience
|
||||
|
||||
Currently, the Machine on Windows experience is not as good as the Mac / Linux. There is no "recommended" path to use Machine and there are several inconsistencies on Windows such as logging and output formatting.
|
||||
|
||||
# Project Planning
|
||||
|
||||
An [Open-Source Planning Process](https://github.com/docker/machine/wiki/Open-Source-Planning-Process) is used to define the Roadmap. [Project Pages](https://github.com/docker/machine/wiki) define the goals for each Milestone and identify current progress.
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
version: "{build}"
|
||||
|
||||
skip_tags: true
|
||||
|
||||
os: Windows Server 2012 R2
|
||||
|
||||
environment:
|
||||
GOPATH: c:\gopath
|
||||
|
||||
clone_folder: c:\gopath\src\github.com\docker\machine
|
||||
|
||||
build_script:
|
||||
- go build -i -o ./bin/docker-machine.exe ./cmd/machine.go
|
||||
|
||||
test_script:
|
||||
- powershell -Command go test -v ./libmachine/shell
|
||||
|
||||
deploy: off
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
machine:
|
||||
pre:
|
||||
- bash < <(curl -s -S -L https://raw.githubusercontent.com/moovweb/gvm/1.0.22/binscripts/gvm-installer)
|
||||
|
||||
post:
|
||||
- gvm install go1.6 -B --name=stable
|
||||
|
||||
environment:
|
||||
CHECKOUT: /home/ubuntu/$CIRCLE_PROJECT_REPONAME
|
||||
BASE_DIR: src/github.com/$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME
|
||||
BASE_STABLE: ../../../$HOME/.gvm/pkgsets/stable/global/$BASE_DIR
|
||||
GO15VENDOREXPERIMENT: 1
|
||||
|
||||
dependencies:
|
||||
override:
|
||||
- >
|
||||
gvm use stable &&
|
||||
mkdir -p "$(dirname $BASE_STABLE)" &&
|
||||
cp -R "$CHECKOUT" "$BASE_STABLE"
|
||||
|
||||
test:
|
||||
pre:
|
||||
- gvm use stable && make build:
|
||||
pwd: $BASE_STABLE
|
||||
- gvm use stable && go get github.com/docker/docker-machine-driver-ci-test
|
||||
|
||||
override:
|
||||
- gvm use stable && PATH=../../../../bin:$PATH DRIVER=ci-test go test -v github.com/docker/machine/its/...:
|
||||
pwd: $BASE_STABLE
|
||||
timeout: 600
|
||||
|
|
@ -0,0 +1,215 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"path/filepath"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/docker/machine/commands"
|
||||
"github.com/docker/machine/commands/mcndirs"
|
||||
"github.com/docker/machine/drivers/amazonec2"
|
||||
"github.com/docker/machine/drivers/azure"
|
||||
"github.com/docker/machine/drivers/digitalocean"
|
||||
"github.com/docker/machine/drivers/exoscale"
|
||||
"github.com/docker/machine/drivers/generic"
|
||||
"github.com/docker/machine/drivers/google"
|
||||
"github.com/docker/machine/drivers/hyperv"
|
||||
"github.com/docker/machine/drivers/none"
|
||||
"github.com/docker/machine/drivers/openstack"
|
||||
"github.com/docker/machine/drivers/rackspace"
|
||||
"github.com/docker/machine/drivers/softlayer"
|
||||
"github.com/docker/machine/drivers/virtualbox"
|
||||
"github.com/docker/machine/drivers/vmwarefusion"
|
||||
"github.com/docker/machine/drivers/vmwarevcloudair"
|
||||
"github.com/docker/machine/drivers/vmwarevsphere"
|
||||
"github.com/docker/machine/libmachine/drivers/plugin"
|
||||
"github.com/docker/machine/libmachine/drivers/plugin/localbinary"
|
||||
"github.com/docker/machine/libmachine/log"
|
||||
"github.com/docker/machine/version"
|
||||
)
|
||||
|
||||
var AppHelpTemplate = `Usage: {{.Name}} {{if .Flags}}[OPTIONS] {{end}}COMMAND [arg...]
|
||||
|
||||
{{.Usage}}
|
||||
|
||||
Version: {{.Version}}{{if or .Author .Email}}
|
||||
|
||||
Author:{{if .Author}}
|
||||
{{.Author}}{{if .Email}} - <{{.Email}}>{{end}}{{else}}
|
||||
{{.Email}}{{end}}{{end}}
|
||||
{{if .Flags}}
|
||||
Options:
|
||||
{{range .Flags}}{{.}}
|
||||
{{end}}{{end}}
|
||||
Commands:
|
||||
{{range .Commands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}}
|
||||
{{end}}
|
||||
Run '{{.Name}} COMMAND --help' for more information on a command.
|
||||
`
|
||||
|
||||
var CommandHelpTemplate = `Usage: docker-machine {{.Name}}{{if .Flags}} [OPTIONS]{{end}} [arg...]
|
||||
|
||||
{{.Usage}}{{if .Description}}
|
||||
|
||||
Description:
|
||||
{{.Description}}{{end}}{{if .Flags}}
|
||||
|
||||
Options:
|
||||
{{range .Flags}}
|
||||
{{.}}{{end}}{{ end }}
|
||||
`
|
||||
|
||||
func setDebugOutputLevel() {
|
||||
// TODO: I'm not really a fan of this method and really would rather
|
||||
// use -v / --verbose TBQH
|
||||
for _, f := range os.Args {
|
||||
if f == "-D" || f == "--debug" || f == "-debug" {
|
||||
log.SetDebug(true)
|
||||
}
|
||||
}
|
||||
|
||||
debugEnv := os.Getenv("MACHINE_DEBUG")
|
||||
if debugEnv != "" {
|
||||
showDebug, err := strconv.ParseBool(debugEnv)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error parsing boolean value from MACHINE_DEBUG: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
log.SetDebug(showDebug)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
if os.Getenv(localbinary.PluginEnvKey) == localbinary.PluginEnvVal {
|
||||
driverName := os.Getenv(localbinary.PluginEnvDriverName)
|
||||
runDriver(driverName)
|
||||
return
|
||||
}
|
||||
|
||||
localbinary.CurrentBinaryIsDockerMachine = true
|
||||
|
||||
setDebugOutputLevel()
|
||||
cli.AppHelpTemplate = AppHelpTemplate
|
||||
cli.CommandHelpTemplate = CommandHelpTemplate
|
||||
app := cli.NewApp()
|
||||
app.Name = filepath.Base(os.Args[0])
|
||||
app.Author = "Docker Machine Contributors"
|
||||
app.Email = "https://github.com/docker/machine"
|
||||
|
||||
app.Commands = commands.Commands
|
||||
app.CommandNotFound = cmdNotFound
|
||||
app.Usage = "Create and manage machines running Docker."
|
||||
app.Version = version.FullVersion()
|
||||
|
||||
log.Debug("Docker Machine Version: ", app.Version)
|
||||
|
||||
app.Flags = []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "debug, D",
|
||||
Usage: "Enable debug mode",
|
||||
},
|
||||
cli.StringFlag{
|
||||
EnvVar: "MACHINE_STORAGE_PATH",
|
||||
Name: "storage-path, s",
|
||||
Value: mcndirs.GetBaseDir(),
|
||||
Usage: "Configures storage path",
|
||||
},
|
||||
cli.StringFlag{
|
||||
EnvVar: "MACHINE_TLS_CA_CERT",
|
||||
Name: "tls-ca-cert",
|
||||
Usage: "CA to verify remotes against",
|
||||
Value: "",
|
||||
},
|
||||
cli.StringFlag{
|
||||
EnvVar: "MACHINE_TLS_CA_KEY",
|
||||
Name: "tls-ca-key",
|
||||
Usage: "Private key to generate certificates",
|
||||
Value: "",
|
||||
},
|
||||
cli.StringFlag{
|
||||
EnvVar: "MACHINE_TLS_CLIENT_CERT",
|
||||
Name: "tls-client-cert",
|
||||
Usage: "Client cert to use for TLS",
|
||||
Value: "",
|
||||
},
|
||||
cli.StringFlag{
|
||||
EnvVar: "MACHINE_TLS_CLIENT_KEY",
|
||||
Name: "tls-client-key",
|
||||
Usage: "Private key used in client TLS auth",
|
||||
Value: "",
|
||||
},
|
||||
cli.StringFlag{
|
||||
EnvVar: "MACHINE_GITHUB_API_TOKEN",
|
||||
Name: "github-api-token",
|
||||
Usage: "Token to use for requests to the Github API",
|
||||
Value: "",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
EnvVar: "MACHINE_NATIVE_SSH",
|
||||
Name: "native-ssh",
|
||||
Usage: "Use the native (Go-based) SSH implementation.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
EnvVar: "MACHINE_BUGSNAG_API_TOKEN",
|
||||
Name: "bugsnag-api-token",
|
||||
Usage: "BugSnag API token for crash reporting",
|
||||
Value: "",
|
||||
},
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func runDriver(driverName string) {
|
||||
switch driverName {
|
||||
case "amazonec2":
|
||||
plugin.RegisterDriver(amazonec2.NewDriver("", ""))
|
||||
case "azure":
|
||||
plugin.RegisterDriver(azure.NewDriver("", ""))
|
||||
case "digitalocean":
|
||||
plugin.RegisterDriver(digitalocean.NewDriver("", ""))
|
||||
case "exoscale":
|
||||
plugin.RegisterDriver(exoscale.NewDriver("", ""))
|
||||
case "generic":
|
||||
plugin.RegisterDriver(generic.NewDriver("", ""))
|
||||
case "google":
|
||||
plugin.RegisterDriver(google.NewDriver("", ""))
|
||||
case "hyperv":
|
||||
plugin.RegisterDriver(hyperv.NewDriver("", ""))
|
||||
case "none":
|
||||
plugin.RegisterDriver(none.NewDriver("", ""))
|
||||
case "openstack":
|
||||
plugin.RegisterDriver(openstack.NewDriver("", ""))
|
||||
case "rackspace":
|
||||
plugin.RegisterDriver(rackspace.NewDriver("", ""))
|
||||
case "softlayer":
|
||||
plugin.RegisterDriver(softlayer.NewDriver("", ""))
|
||||
case "virtualbox":
|
||||
plugin.RegisterDriver(virtualbox.NewDriver("", ""))
|
||||
case "vmwarefusion":
|
||||
plugin.RegisterDriver(vmwarefusion.NewDriver("", ""))
|
||||
case "vmwarevcloudair":
|
||||
plugin.RegisterDriver(vmwarevcloudair.NewDriver("", ""))
|
||||
case "vmwarevsphere":
|
||||
plugin.RegisterDriver(vmwarevsphere.NewDriver("", ""))
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "Unsupported driver: %s\n", driverName)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func cmdNotFound(c *cli.Context, command string) {
|
||||
log.Errorf(
|
||||
"%s: '%s' is not a %s command. See '%s --help'.",
|
||||
c.App.Name,
|
||||
command,
|
||||
c.App.Name,
|
||||
os.Args[0],
|
||||
)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/machine/commands/mcndirs"
|
||||
)
|
||||
|
||||
func TestStorePathSetCorrectly(t *testing.T) {
|
||||
mcndirs.BaseDir = ""
|
||||
os.Args = []string{"docker-machine", "--storage-path", "/tmp/foo"}
|
||||
main()
|
||||
if mcndirs.BaseDir != "/tmp/foo" {
|
||||
t.Fatal("Expected MACHINE_STORAGE_PATH environment variable to be /tmp/foo but was ", os.Getenv("MACHINE_STORAGE_PATH"))
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"time"
|
||||
|
||||
"github.com/docker/machine/libmachine"
|
||||
"github.com/docker/machine/libmachine/persist"
|
||||
"github.com/docker/machine/libmachine/state"
|
||||
)
|
||||
|
||||
const (
|
||||
activeDefaultTimeout = 10
|
||||
)
|
||||
|
||||
var (
|
||||
errNoActiveHost = errors.New("No active host found")
|
||||
errActiveTimeout = errors.New("Error getting active host: timeout")
|
||||
)
|
||||
|
||||
func cmdActive(c CommandLine, api libmachine.API) error {
|
||||
if len(c.Args()) > 0 {
|
||||
return ErrTooManyArguments
|
||||
}
|
||||
|
||||
hosts, hostsInError, err := persist.LoadAllHosts(api)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error getting active host: %s", err)
|
||||
}
|
||||
|
||||
timeout := time.Duration(c.Int("timeout")) * time.Second
|
||||
items := getHostListItems(hosts, hostsInError, timeout)
|
||||
|
||||
active, err := activeHost(items)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println(active.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
func activeHost(items []HostListItem) (HostListItem, error) {
|
||||
timeout := false
|
||||
for _, item := range items {
|
||||
if item.ActiveHost || item.ActiveSwarm {
|
||||
return item, nil
|
||||
}
|
||||
if item.State == state.Timeout {
|
||||
timeout = true
|
||||
}
|
||||
}
|
||||
if timeout {
|
||||
return HostListItem{}, errActiveTimeout
|
||||
}
|
||||
return HostListItem{}, errNoActiveHost
|
||||
}
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/docker/machine/libmachine/state"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCmdActiveNone(t *testing.T) {
|
||||
hostListItems := []HostListItem{
|
||||
{
|
||||
Name: "host1",
|
||||
ActiveHost: false,
|
||||
ActiveSwarm: false,
|
||||
State: state.Running,
|
||||
},
|
||||
{
|
||||
Name: "host2",
|
||||
ActiveHost: false,
|
||||
ActiveSwarm: false,
|
||||
State: state.Running,
|
||||
},
|
||||
{
|
||||
Name: "host3",
|
||||
ActiveHost: false,
|
||||
ActiveSwarm: false,
|
||||
State: state.Running,
|
||||
},
|
||||
}
|
||||
_, err := activeHost(hostListItems)
|
||||
assert.Equal(t, err, errNoActiveHost)
|
||||
}
|
||||
|
||||
func TestCmdActiveHost(t *testing.T) {
|
||||
hostListItems := []HostListItem{
|
||||
{
|
||||
Name: "host1",
|
||||
ActiveHost: false,
|
||||
ActiveSwarm: false,
|
||||
State: state.Timeout,
|
||||
},
|
||||
{
|
||||
Name: "host2",
|
||||
ActiveHost: true,
|
||||
ActiveSwarm: false,
|
||||
State: state.Running,
|
||||
},
|
||||
{
|
||||
Name: "host3",
|
||||
ActiveHost: false,
|
||||
ActiveSwarm: false,
|
||||
State: state.Running,
|
||||
},
|
||||
}
|
||||
active, err := activeHost(hostListItems)
|
||||
assert.Equal(t, err, nil)
|
||||
assert.Equal(t, active.Name, "host2")
|
||||
}
|
||||
|
||||
func TestCmdActiveSwarm(t *testing.T) {
|
||||
hostListItems := []HostListItem{
|
||||
{
|
||||
Name: "host1",
|
||||
ActiveHost: false,
|
||||
ActiveSwarm: false,
|
||||
State: state.Running,
|
||||
},
|
||||
{
|
||||
Name: "host2",
|
||||
ActiveHost: false,
|
||||
ActiveSwarm: false,
|
||||
State: state.Running,
|
||||
},
|
||||
{
|
||||
Name: "host3",
|
||||
ActiveHost: false,
|
||||
ActiveSwarm: true,
|
||||
State: state.Running,
|
||||
},
|
||||
}
|
||||
active, err := activeHost(hostListItems)
|
||||
assert.Equal(t, err, nil)
|
||||
assert.Equal(t, active.Name, "host3")
|
||||
}
|
||||
|
||||
func TestCmdActiveTimeout(t *testing.T) {
|
||||
hostListItems := []HostListItem{
|
||||
{
|
||||
Name: "host1",
|
||||
ActiveHost: false,
|
||||
ActiveSwarm: false,
|
||||
State: state.Running,
|
||||
},
|
||||
{
|
||||
Name: "host2",
|
||||
ActiveHost: false,
|
||||
ActiveSwarm: false,
|
||||
State: state.Running,
|
||||
},
|
||||
{
|
||||
Name: "host3",
|
||||
ActiveHost: false,
|
||||
ActiveSwarm: false,
|
||||
State: state.Timeout,
|
||||
},
|
||||
}
|
||||
_, err := activeHost(hostListItems)
|
||||
assert.Equal(t, err, errActiveTimeout)
|
||||
}
|
||||
|
|
@ -0,0 +1,464 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/docker/machine/commands/mcndirs"
|
||||
"github.com/docker/machine/libmachine"
|
||||
"github.com/docker/machine/libmachine/crashreport"
|
||||
"github.com/docker/machine/libmachine/host"
|
||||
"github.com/docker/machine/libmachine/log"
|
||||
"github.com/docker/machine/libmachine/mcnerror"
|
||||
"github.com/docker/machine/libmachine/mcnutils"
|
||||
"github.com/docker/machine/libmachine/persist"
|
||||
"github.com/docker/machine/libmachine/ssh"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultMachineName = "default"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrHostLoad = errors.New("All specified hosts had errors loading their configuration.")
|
||||
ErrNoDefault = fmt.Errorf("Error: No machine name(s) specified and no %q machine exists.", defaultMachineName)
|
||||
ErrNoMachineSpecified = errors.New("Error: Expected to get one or more machine names as arguments")
|
||||
ErrExpectedOneMachine = errors.New("Error: Expected one machine name as an argument")
|
||||
ErrTooManyArguments = errors.New("Error: Too many arguments given")
|
||||
|
||||
osExit = func(code int) { os.Exit(code) }
|
||||
)
|
||||
|
||||
// CommandLine contains all the information passed to the commands on the command line.
|
||||
type CommandLine interface {
|
||||
ShowHelp()
|
||||
|
||||
ShowVersion()
|
||||
|
||||
Application() *cli.App
|
||||
|
||||
Args() cli.Args
|
||||
|
||||
IsSet(name string) bool
|
||||
|
||||
Bool(name string) bool
|
||||
|
||||
Int(name string) int
|
||||
|
||||
String(name string) string
|
||||
|
||||
StringSlice(name string) []string
|
||||
|
||||
GlobalString(name string) string
|
||||
|
||||
FlagNames() (names []string)
|
||||
|
||||
Generic(name string) interface{}
|
||||
}
|
||||
|
||||
type contextCommandLine struct {
|
||||
*cli.Context
|
||||
}
|
||||
|
||||
func (c *contextCommandLine) ShowHelp() {
|
||||
cli.ShowCommandHelp(c.Context, c.Command.Name)
|
||||
}
|
||||
|
||||
func (c *contextCommandLine) ShowVersion() {
|
||||
cli.ShowVersion(c.Context)
|
||||
}
|
||||
|
||||
func (c *contextCommandLine) Application() *cli.App {
|
||||
return c.App
|
||||
}
|
||||
|
||||
// targetHost returns a specific host name if one is indicated by the first CLI
|
||||
// arg, or the default host name if no host is specified.
|
||||
func targetHost(c CommandLine, api libmachine.API) (string, error) {
|
||||
if len(c.Args()) == 0 {
|
||||
defaultExists, err := api.Exists(defaultMachineName)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Error checking if host %q exists: %s", defaultMachineName, err)
|
||||
}
|
||||
|
||||
if defaultExists {
|
||||
return defaultMachineName, nil
|
||||
}
|
||||
|
||||
return "", ErrNoDefault
|
||||
}
|
||||
|
||||
return c.Args()[0], nil
|
||||
}
|
||||
|
||||
func runAction(actionName string, c CommandLine, api libmachine.API) error {
|
||||
var (
|
||||
hostsToLoad []string
|
||||
)
|
||||
|
||||
// If user did not specify a machine name explicitly, use the 'default'
|
||||
// machine if it exists. This allows short form commands such as
|
||||
// 'docker-machine stop' for convenience.
|
||||
if len(c.Args()) == 0 {
|
||||
target, err := targetHost(c, api)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hostsToLoad = []string{target}
|
||||
} else {
|
||||
hostsToLoad = c.Args()
|
||||
}
|
||||
|
||||
hosts, hostsInError := persist.LoadHosts(api, hostsToLoad)
|
||||
|
||||
if len(hostsInError) > 0 {
|
||||
errs := []error{}
|
||||
for _, err := range hostsInError {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
return consolidateErrs(errs)
|
||||
}
|
||||
|
||||
if len(hosts) == 0 {
|
||||
return ErrHostLoad
|
||||
}
|
||||
|
||||
if errs := runActionForeachMachine(actionName, hosts); len(errs) > 0 {
|
||||
return consolidateErrs(errs)
|
||||
}
|
||||
|
||||
for _, h := range hosts {
|
||||
if err := api.Save(h); err != nil {
|
||||
return fmt.Errorf("Error saving host to store: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func runCommand(command func(commandLine CommandLine, api libmachine.API) error) func(context *cli.Context) {
|
||||
return func(context *cli.Context) {
|
||||
api := libmachine.NewClient(mcndirs.GetBaseDir(), mcndirs.GetMachineCertDir())
|
||||
defer api.Close()
|
||||
|
||||
if context.GlobalBool("native-ssh") {
|
||||
api.SSHClientType = ssh.Native
|
||||
}
|
||||
api.GithubAPIToken = context.GlobalString("github-api-token")
|
||||
api.Filestore.Path = context.GlobalString("storage-path")
|
||||
|
||||
// TODO (nathanleclaire): These should ultimately be accessed
|
||||
// through the libmachine client by the rest of the code and
|
||||
// not through their respective modules. For now, however,
|
||||
// they are also being set the way that they originally were
|
||||
// set to preserve backwards compatibility.
|
||||
mcndirs.BaseDir = api.Filestore.Path
|
||||
mcnutils.GithubAPIToken = api.GithubAPIToken
|
||||
ssh.SetDefaultClient(api.SSHClientType)
|
||||
|
||||
if err := command(&contextCommandLine{context}, api); err != nil {
|
||||
log.Error(err)
|
||||
|
||||
if crashErr, ok := err.(crashreport.CrashError); ok {
|
||||
crashReporter := crashreport.NewCrashReporter(mcndirs.GetBaseDir(), context.GlobalString("bugsnag-api-token"))
|
||||
crashReporter.Send(crashErr)
|
||||
|
||||
if _, ok := crashErr.Cause.(mcnerror.ErrDuringPreCreate); ok {
|
||||
osExit(3)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
osExit(1)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func confirmInput(msg string) (bool, error) {
|
||||
fmt.Printf("%s (y/n): ", msg)
|
||||
|
||||
var resp string
|
||||
_, err := fmt.Scanln(&resp)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
confirmed := strings.Index(strings.ToLower(resp), "y") == 0
|
||||
return confirmed, nil
|
||||
}
|
||||
|
||||
var Commands = []cli.Command{
|
||||
{
|
||||
Name: "active",
|
||||
Usage: "Print which machine is active",
|
||||
Action: runCommand(cmdActive),
|
||||
Flags: []cli.Flag{
|
||||
cli.IntFlag{
|
||||
Name: "timeout, t",
|
||||
Usage: fmt.Sprintf("Timeout in seconds, default to %ds", activeDefaultTimeout),
|
||||
Value: activeDefaultTimeout,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "config",
|
||||
Usage: "Print the connection config for machine",
|
||||
Description: "Argument is a machine name.",
|
||||
Action: runCommand(cmdConfig),
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "swarm",
|
||||
Usage: "Display the Swarm config instead of the Docker daemon",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Flags: SharedCreateFlags,
|
||||
Name: "create",
|
||||
Usage: "Create a machine",
|
||||
Description: fmt.Sprintf("Run '%s create --driver name' to include the create flags for that driver in the help text.", os.Args[0]),
|
||||
Action: runCommand(cmdCreateOuter),
|
||||
SkipFlagParsing: true,
|
||||
},
|
||||
{
|
||||
Name: "env",
|
||||
Usage: "Display the commands to set up the environment for the Docker client",
|
||||
Description: "Argument is a machine name.",
|
||||
Action: runCommand(cmdEnv),
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "swarm",
|
||||
Usage: "Display the Swarm config instead of the Docker daemon",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "shell",
|
||||
Usage: "Force environment to be configured for a specified shell: [fish, cmd, powershell, tcsh], default is auto-detect",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "unset, u",
|
||||
Usage: "Unset variables instead of setting them",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "no-proxy",
|
||||
Usage: "Add machine IP to NO_PROXY environment variable",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "inspect",
|
||||
Usage: "Inspect information about a machine",
|
||||
Description: "Argument is a machine name.",
|
||||
Action: runCommand(cmdInspect),
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "format, f",
|
||||
Usage: "Format the output using the given go template.",
|
||||
Value: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "ip",
|
||||
Usage: "Get the IP address of a machine",
|
||||
Description: "Argument(s) are one or more machine names.",
|
||||
Action: runCommand(cmdIP),
|
||||
},
|
||||
{
|
||||
Name: "kill",
|
||||
Usage: "Kill a machine",
|
||||
Description: "Argument(s) are one or more machine names.",
|
||||
Action: runCommand(cmdKill),
|
||||
},
|
||||
{
|
||||
Name: "ls",
|
||||
Usage: "List machines",
|
||||
Action: runCommand(cmdLs),
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "quiet, q",
|
||||
Usage: "Enable quiet mode",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "filter",
|
||||
Usage: "Filter output based on conditions provided",
|
||||
Value: &cli.StringSlice{},
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "timeout, t",
|
||||
Usage: fmt.Sprintf("Timeout in seconds, default to %ds", lsDefaultTimeout),
|
||||
Value: lsDefaultTimeout,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "format, f",
|
||||
Usage: "Pretty-print machines using a Go template",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "provision",
|
||||
Usage: "Re-provision existing machines",
|
||||
Action: runCommand(cmdProvision),
|
||||
},
|
||||
{
|
||||
Name: "regenerate-certs",
|
||||
Usage: "Regenerate TLS Certificates for a machine",
|
||||
Description: "Argument(s) are one or more machine names.",
|
||||
Action: runCommand(cmdRegenerateCerts),
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "force, f",
|
||||
Usage: "Force rebuild and do not prompt",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "restart",
|
||||
Usage: "Restart a machine",
|
||||
Description: "Argument(s) are one or more machine names.",
|
||||
Action: runCommand(cmdRestart),
|
||||
},
|
||||
{
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "force, f",
|
||||
Usage: "Remove local configuration even if machine cannot be removed, also implies an automatic yes (`-y`)",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "y",
|
||||
Usage: "Assumes automatic yes to proceed with remove, without prompting further user confirmation",
|
||||
},
|
||||
},
|
||||
Name: "rm",
|
||||
Usage: "Remove a machine",
|
||||
Description: "Argument(s) are one or more machine names.",
|
||||
Action: runCommand(cmdRm),
|
||||
},
|
||||
{
|
||||
Name: "ssh",
|
||||
Usage: "Log into or run a command on a machine with SSH.",
|
||||
Description: "Arguments are [machine-name] [command]",
|
||||
Action: runCommand(cmdSSH),
|
||||
SkipFlagParsing: true,
|
||||
},
|
||||
{
|
||||
Name: "scp",
|
||||
Usage: "Copy files between machines",
|
||||
Description: "Arguments are [machine:][path] [machine:][path].",
|
||||
Action: runCommand(cmdScp),
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "recursive, r",
|
||||
Usage: "Copy files recursively (required to copy directories)",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "start",
|
||||
Usage: "Start a machine",
|
||||
Description: "Argument(s) are one or more machine names.",
|
||||
Action: runCommand(cmdStart),
|
||||
},
|
||||
{
|
||||
Name: "status",
|
||||
Usage: "Get the status of a machine",
|
||||
Description: "Argument is a machine name.",
|
||||
Action: runCommand(cmdStatus),
|
||||
},
|
||||
{
|
||||
Name: "stop",
|
||||
Usage: "Stop a machine",
|
||||
Description: "Argument(s) are one or more machine names.",
|
||||
Action: runCommand(cmdStop),
|
||||
},
|
||||
{
|
||||
Name: "upgrade",
|
||||
Usage: "Upgrade a machine to the latest version of Docker",
|
||||
Description: "Argument(s) are one or more machine names.",
|
||||
Action: runCommand(cmdUpgrade),
|
||||
},
|
||||
{
|
||||
Name: "url",
|
||||
Usage: "Get the URL of a machine",
|
||||
Description: "Argument is a machine name.",
|
||||
Action: runCommand(cmdURL),
|
||||
},
|
||||
{
|
||||
Name: "version",
|
||||
Usage: "Show the Docker Machine version or a machine docker version",
|
||||
Action: runCommand(cmdVersion),
|
||||
},
|
||||
}
|
||||
|
||||
func printIP(h *host.Host) func() error {
|
||||
return func() error {
|
||||
ip, err := h.Driver.GetIP()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error getting IP address: %s", err)
|
||||
}
|
||||
|
||||
fmt.Println(ip)
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// machineCommand maps the command name to the corresponding machine command.
|
||||
// We run commands concurrently and communicate back an error if there was one.
|
||||
func machineCommand(actionName string, host *host.Host, errorChan chan<- error) {
|
||||
// TODO: These actions should have their own type.
|
||||
commands := map[string](func() error){
|
||||
"configureAuth": host.ConfigureAuth,
|
||||
"start": host.Start,
|
||||
"stop": host.Stop,
|
||||
"restart": host.Restart,
|
||||
"kill": host.Kill,
|
||||
"upgrade": host.Upgrade,
|
||||
"ip": printIP(host),
|
||||
"provision": host.Provision,
|
||||
}
|
||||
|
||||
log.Debugf("command=%s machine=%s", actionName, host.Name)
|
||||
|
||||
errorChan <- commands[actionName]()
|
||||
}
|
||||
|
||||
// runActionForeachMachine will run the command across multiple machines
|
||||
func runActionForeachMachine(actionName string, machines []*host.Host) []error {
|
||||
var (
|
||||
numConcurrentActions = 0
|
||||
errorChan = make(chan error)
|
||||
errs = []error{}
|
||||
)
|
||||
|
||||
for _, machine := range machines {
|
||||
numConcurrentActions++
|
||||
go machineCommand(actionName, machine, errorChan)
|
||||
}
|
||||
|
||||
// TODO: We should probably only do 5-10 of these
|
||||
// at a time, since otherwise cloud providers might
|
||||
// rate limit us.
|
||||
for i := 0; i < numConcurrentActions; i++ {
|
||||
if err := <-errorChan; err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
|
||||
close(errorChan)
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
func consolidateErrs(errs []error) error {
|
||||
finalErr := ""
|
||||
for _, err := range errs {
|
||||
finalErr = fmt.Sprintf("%s\n%s", finalErr, err)
|
||||
}
|
||||
|
||||
return errors.New(strings.TrimSpace(finalErr))
|
||||
}
|
||||
|
|
@ -0,0 +1,243 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"testing"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/docker/machine/commands/commandstest"
|
||||
"github.com/docker/machine/drivers/fakedriver"
|
||||
"github.com/docker/machine/libmachine"
|
||||
"github.com/docker/machine/libmachine/crashreport"
|
||||
"github.com/docker/machine/libmachine/host"
|
||||
"github.com/docker/machine/libmachine/hosttest"
|
||||
"github.com/docker/machine/libmachine/mcnerror"
|
||||
"github.com/docker/machine/libmachine/provision"
|
||||
"github.com/docker/machine/libmachine/state"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestRunActionForeachMachine(t *testing.T) {
|
||||
defer provision.SetDetector(&provision.StandardDetector{})
|
||||
provision.SetDetector(&provision.FakeDetector{
|
||||
Provisioner: provision.NewNetstatProvisioner(),
|
||||
})
|
||||
|
||||
// Assume a bunch of machines in randomly started or
|
||||
// stopped states.
|
||||
machines := []*host.Host{
|
||||
{
|
||||
Name: "foo",
|
||||
DriverName: "fakedriver",
|
||||
Driver: &fakedriver.Driver{
|
||||
MockState: state.Running,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "bar",
|
||||
DriverName: "fakedriver",
|
||||
Driver: &fakedriver.Driver{
|
||||
MockState: state.Stopped,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "baz",
|
||||
// Ssh, don't tell anyone but this
|
||||
// driver only _thinks_ it's named
|
||||
// virtualbox... (to test serial actions)
|
||||
// It's actually FakeDriver!
|
||||
DriverName: "virtualbox",
|
||||
Driver: &fakedriver.Driver{
|
||||
MockState: state.Stopped,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "spam",
|
||||
DriverName: "virtualbox",
|
||||
Driver: &fakedriver.Driver{
|
||||
MockState: state.Running,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "eggs",
|
||||
DriverName: "fakedriver",
|
||||
Driver: &fakedriver.Driver{
|
||||
MockState: state.Stopped,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "ham",
|
||||
DriverName: "fakedriver",
|
||||
Driver: &fakedriver.Driver{
|
||||
MockState: state.Running,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
runActionForeachMachine("start", machines)
|
||||
|
||||
for _, machine := range machines {
|
||||
machineState, _ := machine.Driver.GetState()
|
||||
|
||||
assert.Equal(t, state.Running, machineState)
|
||||
}
|
||||
|
||||
runActionForeachMachine("stop", machines)
|
||||
|
||||
for _, machine := range machines {
|
||||
machineState, _ := machine.Driver.GetState()
|
||||
|
||||
assert.Equal(t, state.Stopped, machineState)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrintIPEmptyGivenLocalEngine(t *testing.T) {
|
||||
stdoutGetter := commandstest.NewStdoutGetter()
|
||||
defer stdoutGetter.Stop()
|
||||
|
||||
host, _ := hosttest.GetDefaultTestHost()
|
||||
err := printIP(host)()
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "\n", stdoutGetter.Output())
|
||||
}
|
||||
|
||||
func TestPrintIPPrintsGivenRemoteEngine(t *testing.T) {
|
||||
stdoutGetter := commandstest.NewStdoutGetter()
|
||||
defer stdoutGetter.Stop()
|
||||
|
||||
host, _ := hosttest.GetDefaultTestHost()
|
||||
host.Driver = &fakedriver.Driver{
|
||||
MockState: state.Running,
|
||||
MockIP: "1.2.3.4",
|
||||
}
|
||||
err := printIP(host)()
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "1.2.3.4\n", stdoutGetter.Output())
|
||||
}
|
||||
|
||||
func TestConsolidateError(t *testing.T) {
|
||||
cases := []struct {
|
||||
inputErrs []error
|
||||
expectedErr error
|
||||
}{
|
||||
{
|
||||
inputErrs: []error{
|
||||
errors.New("Couldn't remove host 'bar'"),
|
||||
},
|
||||
expectedErr: errors.New("Couldn't remove host 'bar'"),
|
||||
},
|
||||
{
|
||||
inputErrs: []error{
|
||||
errors.New("Couldn't remove host 'bar'"),
|
||||
errors.New("Couldn't remove host 'foo'"),
|
||||
},
|
||||
expectedErr: errors.New("Couldn't remove host 'bar'\nCouldn't remove host 'foo'"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
assert.Equal(t, c.expectedErr, consolidateErrs(c.inputErrs))
|
||||
}
|
||||
}
|
||||
|
||||
type MockCrashReporter struct {
|
||||
sent bool
|
||||
}
|
||||
|
||||
func (m *MockCrashReporter) Send(err crashreport.CrashError) error {
|
||||
m.sent = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestSendCrashReport(t *testing.T) {
|
||||
defer func(fnOsExit func(code int)) { osExit = fnOsExit }(osExit)
|
||||
osExit = func(code int) {}
|
||||
|
||||
defer func(factory func(baseDir string, apiKey string) crashreport.CrashReporter) {
|
||||
crashreport.NewCrashReporter = factory
|
||||
}(crashreport.NewCrashReporter)
|
||||
|
||||
tests := []struct {
|
||||
description string
|
||||
err error
|
||||
sent bool
|
||||
}{
|
||||
{
|
||||
description: "Should send crash error",
|
||||
err: crashreport.CrashError{
|
||||
Cause: errors.New("BUG"),
|
||||
Command: "command",
|
||||
Context: "context",
|
||||
DriverName: "virtualbox",
|
||||
},
|
||||
sent: true,
|
||||
},
|
||||
{
|
||||
description: "Should not send standard error",
|
||||
err: errors.New("BUG"),
|
||||
sent: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
mockCrashReporter := &MockCrashReporter{}
|
||||
crashreport.NewCrashReporter = func(baseDir string, apiKey string) crashreport.CrashReporter {
|
||||
return mockCrashReporter
|
||||
}
|
||||
|
||||
command := func(commandLine CommandLine, api libmachine.API) error {
|
||||
return test.err
|
||||
}
|
||||
|
||||
context := cli.NewContext(cli.NewApp(), &flag.FlagSet{}, nil)
|
||||
runCommand(command)(context)
|
||||
|
||||
assert.Equal(t, test.sent, mockCrashReporter.sent, test.description)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReturnExitCode1onError(t *testing.T) {
|
||||
command := func(commandLine CommandLine, api libmachine.API) error {
|
||||
return errors.New("foo is not bar")
|
||||
}
|
||||
|
||||
exitCode := checkErrorCodeForCommand(command)
|
||||
|
||||
assert.Equal(t, 1, exitCode)
|
||||
}
|
||||
|
||||
func TestReturnExitCode3onErrorDuringPreCreate(t *testing.T) {
|
||||
command := func(commandLine CommandLine, api libmachine.API) error {
|
||||
return crashreport.CrashError{
|
||||
Cause: mcnerror.ErrDuringPreCreate{
|
||||
Cause: errors.New("foo is not bar"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
exitCode := checkErrorCodeForCommand(command)
|
||||
|
||||
assert.Equal(t, 3, exitCode)
|
||||
}
|
||||
|
||||
func checkErrorCodeForCommand(command func(commandLine CommandLine, api libmachine.API) error) int {
|
||||
var setExitCode int
|
||||
|
||||
originalOSExit := osExit
|
||||
|
||||
defer func() {
|
||||
osExit = originalOSExit
|
||||
}()
|
||||
|
||||
osExit = func(code int) {
|
||||
setExitCode = code
|
||||
}
|
||||
|
||||
context := cli.NewContext(cli.NewApp(), &flag.FlagSet{}, nil)
|
||||
runCommand(command)(context)
|
||||
|
||||
return setExitCode
|
||||
}
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
package commandstest
|
||||
|
||||
import (
|
||||
"github.com/codegangsta/cli"
|
||||
)
|
||||
|
||||
type FakeFlagger struct {
|
||||
Data map[string]interface{}
|
||||
}
|
||||
|
||||
type FakeCommandLine struct {
|
||||
LocalFlags, GlobalFlags *FakeFlagger
|
||||
HelpShown, VersionShown bool
|
||||
CliArgs []string
|
||||
}
|
||||
|
||||
func (ff FakeFlagger) String(key string) string {
|
||||
if value, ok := ff.Data[key]; ok {
|
||||
return value.(string)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (ff FakeFlagger) StringSlice(key string) []string {
|
||||
if value, ok := ff.Data[key]; ok {
|
||||
return value.([]string)
|
||||
}
|
||||
return []string{}
|
||||
}
|
||||
|
||||
func (ff FakeFlagger) Int(key string) int {
|
||||
if value, ok := ff.Data[key]; ok {
|
||||
return value.(int)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (ff FakeFlagger) Bool(key string) bool {
|
||||
if value, ok := ff.Data[key]; ok {
|
||||
return value.(bool)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (fcli *FakeCommandLine) IsSet(key string) bool {
|
||||
_, ok := fcli.LocalFlags.Data[key]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (fcli *FakeCommandLine) String(key string) string {
|
||||
return fcli.LocalFlags.String(key)
|
||||
}
|
||||
|
||||
func (fcli *FakeCommandLine) StringSlice(key string) []string {
|
||||
return fcli.LocalFlags.StringSlice(key)
|
||||
}
|
||||
|
||||
func (fcli *FakeCommandLine) Int(key string) int {
|
||||
return fcli.LocalFlags.Int(key)
|
||||
}
|
||||
|
||||
func (fcli *FakeCommandLine) Bool(key string) bool {
|
||||
if fcli.LocalFlags == nil {
|
||||
return false
|
||||
}
|
||||
return fcli.LocalFlags.Bool(key)
|
||||
}
|
||||
|
||||
func (fcli *FakeCommandLine) GlobalString(key string) string {
|
||||
return fcli.GlobalFlags.String(key)
|
||||
}
|
||||
|
||||
func (fcli *FakeCommandLine) Generic(name string) interface{} {
|
||||
return fcli.LocalFlags.Data[name]
|
||||
}
|
||||
|
||||
func (fcli *FakeCommandLine) FlagNames() []string {
|
||||
flagNames := []string{}
|
||||
for key := range fcli.LocalFlags.Data {
|
||||
flagNames = append(flagNames, key)
|
||||
}
|
||||
|
||||
return flagNames
|
||||
}
|
||||
|
||||
func (fcli *FakeCommandLine) ShowHelp() {
|
||||
fcli.HelpShown = true
|
||||
}
|
||||
|
||||
func (fcli *FakeCommandLine) Application() *cli.App {
|
||||
return cli.NewApp()
|
||||
}
|
||||
|
||||
func (fcli *FakeCommandLine) Args() cli.Args {
|
||||
return fcli.CliArgs
|
||||
}
|
||||
|
||||
func (fcli *FakeCommandLine) ShowVersion() {
|
||||
fcli.VersionShown = true
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
package commandstest
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
|
||||
"os"
|
||||
)
|
||||
|
||||
var (
|
||||
stdout *os.File
|
||||
)
|
||||
|
||||
func init() {
|
||||
stdout = os.Stdout
|
||||
}
|
||||
|
||||
type StdoutGetter interface {
|
||||
Output() string
|
||||
Stop()
|
||||
}
|
||||
|
||||
type stdoutCapturer struct {
|
||||
stdout *os.File
|
||||
output chan string
|
||||
}
|
||||
|
||||
func NewStdoutGetter() StdoutGetter {
|
||||
r, w, _ := os.Pipe()
|
||||
os.Stdout = w
|
||||
|
||||
output := make(chan string)
|
||||
go func() {
|
||||
var testOutput bytes.Buffer
|
||||
io.Copy(&testOutput, r)
|
||||
output <- testOutput.String()
|
||||
}()
|
||||
|
||||
return &stdoutCapturer{
|
||||
stdout: w,
|
||||
output: output,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *stdoutCapturer) Output() string {
|
||||
c.stdout.Close()
|
||||
text := <-c.output
|
||||
close(c.output)
|
||||
return text
|
||||
}
|
||||
|
||||
func (c *stdoutCapturer) Stop() {
|
||||
os.Stdout = stdout
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/docker/machine/libmachine"
|
||||
"github.com/docker/machine/libmachine/check"
|
||||
"github.com/docker/machine/libmachine/log"
|
||||
)
|
||||
|
||||
func cmdConfig(c CommandLine, api libmachine.API) error {
|
||||
// Ensure that log messages always go to stderr when this command is
|
||||
// being run (it is intended to be run in a subshell)
|
||||
log.SetOutWriter(os.Stderr)
|
||||
|
||||
target, err := targetHost(c, api)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
host, err := api.Load(target)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dockerHost, authOptions, err := check.DefaultConnChecker.Check(host, c.Bool("swarm"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error running connection boilerplate: %s", err)
|
||||
}
|
||||
|
||||
log.Debug(dockerHost)
|
||||
|
||||
fmt.Printf("--tlsverify\n--tlscacert=%q\n--tlscert=%q\n--tlskey=%q\n-H=%s\n",
|
||||
authOptions.CaCertPath, authOptions.ClientCertPath, authOptions.ClientKeyPath, dockerHost)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,465 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"errors"
|
||||
|
||||
"time"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/docker/machine/commands/mcndirs"
|
||||
"github.com/docker/machine/libmachine"
|
||||
"github.com/docker/machine/libmachine/auth"
|
||||
"github.com/docker/machine/libmachine/crashreport"
|
||||
"github.com/docker/machine/libmachine/drivers"
|
||||
"github.com/docker/machine/libmachine/drivers/rpc"
|
||||
"github.com/docker/machine/libmachine/engine"
|
||||
"github.com/docker/machine/libmachine/host"
|
||||
"github.com/docker/machine/libmachine/log"
|
||||
"github.com/docker/machine/libmachine/mcnerror"
|
||||
"github.com/docker/machine/libmachine/mcnflag"
|
||||
"github.com/docker/machine/libmachine/swarm"
|
||||
)
|
||||
|
||||
var (
|
||||
errNoMachineName = errors.New("Error: No machine name specified")
|
||||
)
|
||||
|
||||
var (
|
||||
SharedCreateFlags = []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "driver, d",
|
||||
Usage: fmt.Sprintf(
|
||||
"Driver to create machine with.",
|
||||
),
|
||||
Value: "none",
|
||||
EnvVar: "MACHINE_DRIVER",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "engine-install-url",
|
||||
Usage: "Custom URL to use for engine installation",
|
||||
Value: drivers.DefaultEngineInstallURL,
|
||||
EnvVar: "MACHINE_DOCKER_INSTALL_URL",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "engine-opt",
|
||||
Usage: "Specify arbitrary flags to include with the created engine in the form flag=value",
|
||||
Value: &cli.StringSlice{},
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "engine-insecure-registry",
|
||||
Usage: "Specify insecure registries to allow with the created engine",
|
||||
Value: &cli.StringSlice{},
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "engine-registry-mirror",
|
||||
Usage: "Specify registry mirrors to use",
|
||||
Value: &cli.StringSlice{},
|
||||
EnvVar: "ENGINE_REGISTRY_MIRROR",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "engine-label",
|
||||
Usage: "Specify labels for the created engine",
|
||||
Value: &cli.StringSlice{},
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "engine-storage-driver",
|
||||
Usage: "Specify a storage driver to use with the engine",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "engine-env",
|
||||
Usage: "Specify environment variables to set in the engine",
|
||||
Value: &cli.StringSlice{},
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "swarm",
|
||||
Usage: "Configure Machine to join a Swarm cluster",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "swarm-image",
|
||||
Usage: "Specify Docker image to use for Swarm",
|
||||
Value: "swarm:latest",
|
||||
EnvVar: "MACHINE_SWARM_IMAGE",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "swarm-master",
|
||||
Usage: "Configure Machine to be a Swarm master",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "swarm-discovery",
|
||||
Usage: "Discovery service to use with Swarm",
|
||||
Value: "",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "swarm-strategy",
|
||||
Usage: "Define a default scheduling strategy for Swarm",
|
||||
Value: "spread",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "swarm-opt",
|
||||
Usage: "Define arbitrary flags for Swarm master",
|
||||
Value: &cli.StringSlice{},
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "swarm-join-opt",
|
||||
Usage: "Define arbitrary flags for Swarm join",
|
||||
Value: &cli.StringSlice{},
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "swarm-host",
|
||||
Usage: "ip/socket to listen on for Swarm master",
|
||||
Value: "tcp://0.0.0.0:3376",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "swarm-addr",
|
||||
Usage: "addr to advertise for Swarm (default: detect and use the machine IP)",
|
||||
Value: "",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "swarm-experimental",
|
||||
Usage: "Enable Swarm experimental features",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "tls-san",
|
||||
Usage: "Support extra SANs for TLS certs",
|
||||
Value: &cli.StringSlice{},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func cmdCreateInner(c CommandLine, api libmachine.API) error {
|
||||
if len(c.Args()) > 1 {
|
||||
return fmt.Errorf("Invalid command line. Found extra arguments %v", c.Args()[1:])
|
||||
}
|
||||
|
||||
name := c.Args().First()
|
||||
if name == "" {
|
||||
c.ShowHelp()
|
||||
return errNoMachineName
|
||||
}
|
||||
|
||||
validName := host.ValidateHostName(name)
|
||||
if !validName {
|
||||
return fmt.Errorf("Error creating machine: %s", mcnerror.ErrInvalidHostname)
|
||||
}
|
||||
|
||||
if err := validateSwarmDiscovery(c.String("swarm-discovery")); err != nil {
|
||||
return fmt.Errorf("Error parsing swarm discovery: %s", err)
|
||||
}
|
||||
|
||||
// TODO: Fix hacky JSON solution
|
||||
rawDriver, err := json.Marshal(&drivers.BaseDriver{
|
||||
MachineName: name,
|
||||
StorePath: c.GlobalString("storage-path"),
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error attempting to marshal bare driver data: %s", err)
|
||||
}
|
||||
|
||||
driverName := c.String("driver")
|
||||
h, err := api.NewHost(driverName, rawDriver)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error getting new host: %s", err)
|
||||
}
|
||||
|
||||
h.HostOptions = &host.Options{
|
||||
AuthOptions: &auth.Options{
|
||||
CertDir: mcndirs.GetMachineCertDir(),
|
||||
CaCertPath: tlsPath(c, "tls-ca-cert", "ca.pem"),
|
||||
CaPrivateKeyPath: tlsPath(c, "tls-ca-key", "ca-key.pem"),
|
||||
ClientCertPath: tlsPath(c, "tls-client-cert", "cert.pem"),
|
||||
ClientKeyPath: tlsPath(c, "tls-client-key", "key.pem"),
|
||||
ServerCertPath: filepath.Join(mcndirs.GetMachineDir(), name, "server.pem"),
|
||||
ServerKeyPath: filepath.Join(mcndirs.GetMachineDir(), name, "server-key.pem"),
|
||||
StorePath: filepath.Join(mcndirs.GetMachineDir(), name),
|
||||
ServerCertSANs: c.StringSlice("tls-san"),
|
||||
},
|
||||
EngineOptions: &engine.Options{
|
||||
ArbitraryFlags: c.StringSlice("engine-opt"),
|
||||
Env: c.StringSlice("engine-env"),
|
||||
InsecureRegistry: c.StringSlice("engine-insecure-registry"),
|
||||
Labels: c.StringSlice("engine-label"),
|
||||
RegistryMirror: c.StringSlice("engine-registry-mirror"),
|
||||
StorageDriver: c.String("engine-storage-driver"),
|
||||
TLSVerify: true,
|
||||
InstallURL: c.String("engine-install-url"),
|
||||
},
|
||||
SwarmOptions: &swarm.Options{
|
||||
IsSwarm: c.Bool("swarm") || c.Bool("swarm-master"),
|
||||
Image: c.String("swarm-image"),
|
||||
Agent: c.Bool("swarm"),
|
||||
Master: c.Bool("swarm-master"),
|
||||
Discovery: c.String("swarm-discovery"),
|
||||
Address: c.String("swarm-addr"),
|
||||
Host: c.String("swarm-host"),
|
||||
Strategy: c.String("swarm-strategy"),
|
||||
ArbitraryFlags: c.StringSlice("swarm-opt"),
|
||||
ArbitraryJoinFlags: c.StringSlice("swarm-join-opt"),
|
||||
IsExperimental: c.Bool("swarm-experimental"),
|
||||
},
|
||||
}
|
||||
|
||||
exists, err := api.Exists(h.Name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error checking if host exists: %s", err)
|
||||
}
|
||||
if exists {
|
||||
return mcnerror.ErrHostAlreadyExists{
|
||||
Name: h.Name,
|
||||
}
|
||||
}
|
||||
|
||||
// driverOpts is the actual data we send over the wire to set the
|
||||
// driver parameters (an interface fulfilling drivers.DriverOptions,
|
||||
// concrete type rpcdriver.RpcFlags).
|
||||
mcnFlags := h.Driver.GetCreateFlags()
|
||||
driverOpts := getDriverOpts(c, mcnFlags)
|
||||
|
||||
if err := h.Driver.SetConfigFromFlags(driverOpts); err != nil {
|
||||
return fmt.Errorf("Error setting machine configuration from flags provided: %s", err)
|
||||
}
|
||||
|
||||
if err := api.Create(h); err != nil {
|
||||
// Wait for all the logs to reach the client
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
vBoxLog := ""
|
||||
if h.DriverName == "virtualbox" {
|
||||
vBoxLog = filepath.Join(api.GetMachinesDir(), h.Name, h.Name, "Logs", "VBox.log")
|
||||
}
|
||||
|
||||
return crashreport.CrashError{
|
||||
Cause: err,
|
||||
Command: "Create",
|
||||
Context: "api.performCreate",
|
||||
DriverName: h.DriverName,
|
||||
LogFilePath: vBoxLog,
|
||||
}
|
||||
}
|
||||
|
||||
if err := api.Save(h); err != nil {
|
||||
return fmt.Errorf("Error attempting to save store: %s", err)
|
||||
}
|
||||
|
||||
log.Infof("To see how to connect your Docker Client to the Docker Engine running on this virtual machine, run: %s env %s", os.Args[0], name)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// The following function is needed because the CLI acrobatics that we're doing
|
||||
// (with having an "outer" and "inner" function each with their own custom
|
||||
// settings and flag parsing needs) are not well supported by codegangsta/cli.
|
||||
//
|
||||
// Instead of trying to make a convoluted series of flag parsing and relying on
|
||||
// codegangsta/cli internals work well, we simply read the flags we're
|
||||
// interested in from the outer function into module-level variables, and then
|
||||
// use them from the "inner" function.
|
||||
//
|
||||
// I'm not very pleased about this, but it seems to be the only decent
|
||||
// compromise without drastically modifying codegangsta/cli internals or our
|
||||
// own CLI.
|
||||
func flagHackLookup(flagName string) string {
|
||||
// e.g. "-d" for "--driver"
|
||||
flagPrefix := flagName[1:3]
|
||||
|
||||
// TODO: Should we support -flag-name (single hyphen) syntax as well?
|
||||
for i, arg := range os.Args {
|
||||
if strings.Contains(arg, flagPrefix) {
|
||||
// format '--driver foo' or '-d foo'
|
||||
if arg == flagPrefix || arg == flagName {
|
||||
if i+1 < len(os.Args) {
|
||||
return os.Args[i+1]
|
||||
}
|
||||
}
|
||||
|
||||
// format '--driver=foo' or '-d=foo'
|
||||
if strings.HasPrefix(arg, flagPrefix+"=") || strings.HasPrefix(arg, flagName+"=") {
|
||||
return strings.Split(arg, "=")[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func cmdCreateOuter(c CommandLine, api libmachine.API) error {
|
||||
const (
|
||||
flagLookupMachineName = "flag-lookup"
|
||||
)
|
||||
|
||||
// We didn't recognize the driver name.
|
||||
driverName := flagHackLookup("--driver")
|
||||
if driverName == "" {
|
||||
//TODO: Check Environment have to include flagHackLookup function.
|
||||
driverName = os.Getenv("MACHINE_DRIVER")
|
||||
if driverName == "" {
|
||||
c.ShowHelp()
|
||||
return nil // ?
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Fix hacky JSON solution
|
||||
rawDriver, err := json.Marshal(&drivers.BaseDriver{
|
||||
MachineName: flagLookupMachineName,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error attempting to marshal bare driver data: %s", err)
|
||||
}
|
||||
|
||||
h, err := api.NewHost(driverName, rawDriver)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: So much flag manipulation and voodoo here, it seems to be
|
||||
// asking for trouble.
|
||||
//
|
||||
// mcnFlags is the data we get back over the wire (type mcnflag.Flag)
|
||||
// to indicate which parameters are available.
|
||||
mcnFlags := h.Driver.GetCreateFlags()
|
||||
|
||||
// This bit will actually make "create" display the correct flags based
|
||||
// on the requested driver.
|
||||
cliFlags, err := convertMcnFlagsToCliFlags(mcnFlags)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error trying to convert provided driver flags to cli flags: %s", err)
|
||||
}
|
||||
|
||||
for i := range c.Application().Commands {
|
||||
cmd := &c.Application().Commands[i]
|
||||
if cmd.HasName("create") {
|
||||
cmd = addDriverFlagsToCommand(cliFlags, cmd)
|
||||
}
|
||||
}
|
||||
|
||||
return c.Application().Run(os.Args)
|
||||
}
|
||||
|
||||
func getDriverOpts(c CommandLine, mcnflags []mcnflag.Flag) drivers.DriverOptions {
|
||||
// TODO: This function is pretty damn YOLO and would benefit from some
|
||||
// sanity checking around types and assertions.
|
||||
//
|
||||
// But, we need it so that we can actually send the flags for creating
|
||||
// a machine over the wire (cli.Context is a no go since there is so
|
||||
// much stuff in it).
|
||||
driverOpts := rpcdriver.RPCFlags{
|
||||
Values: make(map[string]interface{}),
|
||||
}
|
||||
|
||||
for _, f := range mcnflags {
|
||||
driverOpts.Values[f.String()] = f.Default()
|
||||
|
||||
// Hardcoded logic for boolean... :(
|
||||
if f.Default() == nil {
|
||||
driverOpts.Values[f.String()] = false
|
||||
}
|
||||
}
|
||||
|
||||
for _, name := range c.FlagNames() {
|
||||
getter, ok := c.Generic(name).(flag.Getter)
|
||||
if ok {
|
||||
driverOpts.Values[name] = getter.Get()
|
||||
} else {
|
||||
// TODO: This is pretty hacky. StringSlice is the only
|
||||
// type so far we have to worry about which is not a
|
||||
// Getter, though.
|
||||
if c.IsSet(name) {
|
||||
driverOpts.Values[name] = c.StringSlice(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return driverOpts
|
||||
}
|
||||
|
||||
func convertMcnFlagsToCliFlags(mcnFlags []mcnflag.Flag) ([]cli.Flag, error) {
|
||||
cliFlags := []cli.Flag{}
|
||||
for _, f := range mcnFlags {
|
||||
switch t := f.(type) {
|
||||
// TODO: It seems pretty wrong to just default "nil" to this,
|
||||
// but cli.BoolFlag doesn't have a "Value" field (false is
|
||||
// always the default)
|
||||
case *mcnflag.BoolFlag:
|
||||
f := f.(*mcnflag.BoolFlag)
|
||||
cliFlags = append(cliFlags, cli.BoolFlag{
|
||||
Name: f.Name,
|
||||
EnvVar: f.EnvVar,
|
||||
Usage: f.Usage,
|
||||
})
|
||||
case *mcnflag.IntFlag:
|
||||
f := f.(*mcnflag.IntFlag)
|
||||
cliFlags = append(cliFlags, cli.IntFlag{
|
||||
Name: f.Name,
|
||||
EnvVar: f.EnvVar,
|
||||
Usage: f.Usage,
|
||||
Value: f.Value,
|
||||
})
|
||||
case *mcnflag.StringFlag:
|
||||
f := f.(*mcnflag.StringFlag)
|
||||
cliFlags = append(cliFlags, cli.StringFlag{
|
||||
Name: f.Name,
|
||||
EnvVar: f.EnvVar,
|
||||
Usage: f.Usage,
|
||||
Value: f.Value,
|
||||
})
|
||||
case *mcnflag.StringSliceFlag:
|
||||
f := f.(*mcnflag.StringSliceFlag)
|
||||
cliFlags = append(cliFlags, cli.StringSliceFlag{
|
||||
Name: f.Name,
|
||||
EnvVar: f.EnvVar,
|
||||
Usage: f.Usage,
|
||||
|
||||
//TODO: Is this used with defaults? Can we convert the literal []string to cli.StringSlice properly?
|
||||
Value: &cli.StringSlice{},
|
||||
})
|
||||
default:
|
||||
log.Warn("Flag is ", f)
|
||||
return nil, fmt.Errorf("Flag is unrecognized flag type: %T", t)
|
||||
}
|
||||
}
|
||||
|
||||
return cliFlags, nil
|
||||
}
|
||||
|
||||
func addDriverFlagsToCommand(cliFlags []cli.Flag, cmd *cli.Command) *cli.Command {
|
||||
cmd.Flags = append(SharedCreateFlags, cliFlags...)
|
||||
cmd.SkipFlagParsing = false
|
||||
cmd.Action = runCommand(cmdCreateInner)
|
||||
sort.Sort(ByFlagName(cmd.Flags))
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func validateSwarmDiscovery(discovery string) error {
|
||||
if discovery == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
matched, err := regexp.MatchString(`[^:]*://.*`, discovery)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if matched {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("Swarm Discovery URL was in the wrong format: %s", discovery)
|
||||
}
|
||||
|
||||
func tlsPath(c CommandLine, flag string, defaultName string) string {
|
||||
path := c.GlobalString(flag)
|
||||
if path != "" {
|
||||
return path
|
||||
}
|
||||
|
||||
return filepath.Join(mcndirs.GetMachineCertDir(), defaultName)
|
||||
}
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"flag"
|
||||
"github.com/docker/machine/commands/commandstest"
|
||||
"github.com/docker/machine/libmachine/mcnflag"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestValidateSwarmDiscoveryErrorsGivenInvalidURL(t *testing.T) {
|
||||
err := validateSwarmDiscovery("foo")
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestValidateSwarmDiscoveryAcceptsEmptyString(t *testing.T) {
|
||||
err := validateSwarmDiscovery("")
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestValidateSwarmDiscoveryAcceptsValidFormat(t *testing.T) {
|
||||
err := validateSwarmDiscovery("token://deadbeefcafe")
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
type fakeFlagGetter struct {
|
||||
flag.Value
|
||||
value interface{}
|
||||
}
|
||||
|
||||
func (ff fakeFlagGetter) Get() interface{} {
|
||||
return ff.value
|
||||
}
|
||||
|
||||
var nilStringSlice []string
|
||||
|
||||
var getDriverOptsFlags = []mcnflag.Flag{
|
||||
mcnflag.BoolFlag{
|
||||
Name: "bool",
|
||||
},
|
||||
mcnflag.IntFlag{
|
||||
Name: "int",
|
||||
},
|
||||
mcnflag.IntFlag{
|
||||
Name: "int_defaulted",
|
||||
Value: 42,
|
||||
},
|
||||
mcnflag.StringFlag{
|
||||
Name: "string",
|
||||
},
|
||||
mcnflag.StringFlag{
|
||||
Name: "string_defaulted",
|
||||
Value: "bob",
|
||||
},
|
||||
mcnflag.StringSliceFlag{
|
||||
Name: "stringslice",
|
||||
},
|
||||
mcnflag.StringSliceFlag{
|
||||
Name: "stringslice_defaulted",
|
||||
Value: []string{"joe"},
|
||||
},
|
||||
}
|
||||
|
||||
var getDriverOptsTests = []struct {
|
||||
data map[string]interface{}
|
||||
expected map[string]interface{}
|
||||
}{
|
||||
{
|
||||
expected: map[string]interface{}{
|
||||
"bool": false,
|
||||
"int": 0,
|
||||
"int_defaulted": 42,
|
||||
"string": "",
|
||||
"string_defaulted": "bob",
|
||||
"stringslice": nilStringSlice,
|
||||
"stringslice_defaulted": []string{"joe"},
|
||||
},
|
||||
},
|
||||
{
|
||||
data: map[string]interface{}{
|
||||
"bool": fakeFlagGetter{value: true},
|
||||
"int": fakeFlagGetter{value: 42},
|
||||
"int_defaulted": fakeFlagGetter{value: 37},
|
||||
"string": fakeFlagGetter{value: "jake"},
|
||||
"string_defaulted": fakeFlagGetter{value: "george"},
|
||||
// NB: StringSlices are not flag.Getters.
|
||||
"stringslice": []string{"ford"},
|
||||
"stringslice_defaulted": []string{"zaphod", "arthur"},
|
||||
},
|
||||
expected: map[string]interface{}{
|
||||
"bool": true,
|
||||
"int": 42,
|
||||
"int_defaulted": 37,
|
||||
"string": "jake",
|
||||
"string_defaulted": "george",
|
||||
"stringslice": []string{"ford"},
|
||||
"stringslice_defaulted": []string{"zaphod", "arthur"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestGetDriverOpts(t *testing.T) {
|
||||
for _, tt := range getDriverOptsTests {
|
||||
commandLine := &commandstest.FakeCommandLine{
|
||||
LocalFlags: &commandstest.FakeFlagger{
|
||||
Data: tt.data,
|
||||
},
|
||||
}
|
||||
driverOpts := getDriverOpts(commandLine, getDriverOptsFlags)
|
||||
assert.Equal(t, tt.expected["bool"], driverOpts.Bool("bool"))
|
||||
assert.Equal(t, tt.expected["int"], driverOpts.Int("int"))
|
||||
assert.Equal(t, tt.expected["int_defaulted"], driverOpts.Int("int_defaulted"))
|
||||
assert.Equal(t, tt.expected["string"], driverOpts.String("string"))
|
||||
assert.Equal(t, tt.expected["string_defaulted"], driverOpts.String("string_defaulted"))
|
||||
assert.Equal(t, tt.expected["stringslice"], driverOpts.StringSlice("stringslice"))
|
||||
assert.Equal(t, tt.expected["stringslice_defaulted"], driverOpts.StringSlice("stringslice_defaulted"))
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,268 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/docker/machine/commands/mcndirs"
|
||||
"github.com/docker/machine/libmachine"
|
||||
"github.com/docker/machine/libmachine/check"
|
||||
"github.com/docker/machine/libmachine/log"
|
||||
"github.com/docker/machine/libmachine/shell"
|
||||
)
|
||||
|
||||
const (
|
||||
envTmpl = `{{ .Prefix }}DOCKER_TLS_VERIFY{{ .Delimiter }}{{ .DockerTLSVerify }}{{ .Suffix }}{{ .Prefix }}DOCKER_HOST{{ .Delimiter }}{{ .DockerHost }}{{ .Suffix }}{{ .Prefix }}DOCKER_CERT_PATH{{ .Delimiter }}{{ .DockerCertPath }}{{ .Suffix }}{{ .Prefix }}DOCKER_MACHINE_NAME{{ .Delimiter }}{{ .MachineName }}{{ .Suffix }}{{ if .NoProxyVar }}{{ .Prefix }}{{ .NoProxyVar }}{{ .Delimiter }}{{ .NoProxyValue }}{{ .Suffix }}{{end}}{{ .UsageHint }}`
|
||||
)
|
||||
|
||||
var (
|
||||
errImproperUnsetEnvArgs = errors.New("Error: Expected no machine name when the -u flag is present")
|
||||
defaultUsageHinter UsageHintGenerator
|
||||
)
|
||||
|
||||
func init() {
|
||||
defaultUsageHinter = &EnvUsageHintGenerator{}
|
||||
}
|
||||
|
||||
type ShellConfig struct {
|
||||
Prefix string
|
||||
Delimiter string
|
||||
Suffix string
|
||||
DockerCertPath string
|
||||
DockerHost string
|
||||
DockerTLSVerify string
|
||||
UsageHint string
|
||||
MachineName string
|
||||
NoProxyVar string
|
||||
NoProxyValue string
|
||||
}
|
||||
|
||||
func cmdEnv(c CommandLine, api libmachine.API) error {
|
||||
var (
|
||||
err error
|
||||
shellCfg *ShellConfig
|
||||
)
|
||||
|
||||
// Ensure that log messages always go to stderr when this command is
|
||||
// being run (it is intended to be run in a subshell)
|
||||
log.SetOutWriter(os.Stderr)
|
||||
|
||||
if c.Bool("unset") {
|
||||
shellCfg, err = shellCfgUnset(c, api)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
shellCfg, err = shellCfgSet(c, api)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return executeTemplateStdout(shellCfg)
|
||||
}
|
||||
|
||||
func shellCfgSet(c CommandLine, api libmachine.API) (*ShellConfig, error) {
|
||||
if len(c.Args()) > 1 {
|
||||
return nil, ErrExpectedOneMachine
|
||||
}
|
||||
|
||||
target, err := targetHost(c, api)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
host, err := api.Load(target)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dockerHost, _, err := check.DefaultConnChecker.Check(host, c.Bool("swarm"))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error checking TLS connection: %s", err)
|
||||
}
|
||||
|
||||
userShell, err := getShell(c.String("shell"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
shellCfg := &ShellConfig{
|
||||
DockerCertPath: filepath.Join(mcndirs.GetMachineDir(), host.Name),
|
||||
DockerHost: dockerHost,
|
||||
DockerTLSVerify: "1",
|
||||
UsageHint: defaultUsageHinter.GenerateUsageHint(userShell, os.Args),
|
||||
MachineName: host.Name,
|
||||
}
|
||||
|
||||
if c.Bool("no-proxy") {
|
||||
ip, err := host.Driver.GetIP()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error getting host IP: %s", err)
|
||||
}
|
||||
|
||||
noProxyVar, noProxyValue := findNoProxyFromEnv()
|
||||
|
||||
// add the docker host to the no_proxy list idempotently
|
||||
switch {
|
||||
case noProxyValue == "":
|
||||
noProxyValue = ip
|
||||
case strings.Contains(noProxyValue, ip):
|
||||
//ip already in no_proxy list, nothing to do
|
||||
default:
|
||||
noProxyValue = fmt.Sprintf("%s,%s", noProxyValue, ip)
|
||||
}
|
||||
|
||||
shellCfg.NoProxyVar = noProxyVar
|
||||
shellCfg.NoProxyValue = noProxyValue
|
||||
}
|
||||
|
||||
switch userShell {
|
||||
case "fish":
|
||||
shellCfg.Prefix = "set -gx "
|
||||
shellCfg.Suffix = "\";\n"
|
||||
shellCfg.Delimiter = " \""
|
||||
case "powershell":
|
||||
shellCfg.Prefix = "$Env:"
|
||||
shellCfg.Suffix = "\"\n"
|
||||
shellCfg.Delimiter = " = \""
|
||||
case "cmd":
|
||||
shellCfg.Prefix = "SET "
|
||||
shellCfg.Suffix = "\n"
|
||||
shellCfg.Delimiter = "="
|
||||
case "tcsh":
|
||||
shellCfg.Prefix = "setenv "
|
||||
shellCfg.Suffix = "\";\n"
|
||||
shellCfg.Delimiter = " \""
|
||||
case "emacs":
|
||||
shellCfg.Prefix = "(setenv \""
|
||||
shellCfg.Suffix = "\")\n"
|
||||
shellCfg.Delimiter = "\" \""
|
||||
default:
|
||||
shellCfg.Prefix = "export "
|
||||
shellCfg.Suffix = "\"\n"
|
||||
shellCfg.Delimiter = "=\""
|
||||
}
|
||||
|
||||
return shellCfg, nil
|
||||
}
|
||||
|
||||
func shellCfgUnset(c CommandLine, api libmachine.API) (*ShellConfig, error) {
|
||||
if len(c.Args()) != 0 {
|
||||
return nil, errImproperUnsetEnvArgs
|
||||
}
|
||||
|
||||
userShell, err := getShell(c.String("shell"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
shellCfg := &ShellConfig{
|
||||
UsageHint: defaultUsageHinter.GenerateUsageHint(userShell, os.Args),
|
||||
}
|
||||
|
||||
if c.Bool("no-proxy") {
|
||||
shellCfg.NoProxyVar, shellCfg.NoProxyValue = findNoProxyFromEnv()
|
||||
}
|
||||
|
||||
switch userShell {
|
||||
case "fish":
|
||||
shellCfg.Prefix = "set -e "
|
||||
shellCfg.Suffix = ";\n"
|
||||
shellCfg.Delimiter = ""
|
||||
case "powershell":
|
||||
shellCfg.Prefix = `Remove-Item Env:\\`
|
||||
shellCfg.Suffix = "\n"
|
||||
shellCfg.Delimiter = ""
|
||||
case "cmd":
|
||||
shellCfg.Prefix = "SET "
|
||||
shellCfg.Suffix = "\n"
|
||||
shellCfg.Delimiter = "="
|
||||
case "emacs":
|
||||
shellCfg.Prefix = "(setenv \""
|
||||
shellCfg.Suffix = ")\n"
|
||||
shellCfg.Delimiter = "\" nil"
|
||||
case "tcsh":
|
||||
shellCfg.Prefix = "unsetenv "
|
||||
shellCfg.Suffix = ";\n"
|
||||
shellCfg.Delimiter = ""
|
||||
default:
|
||||
shellCfg.Prefix = "unset "
|
||||
shellCfg.Suffix = "\n"
|
||||
shellCfg.Delimiter = ""
|
||||
}
|
||||
|
||||
return shellCfg, nil
|
||||
}
|
||||
|
||||
func executeTemplateStdout(shellCfg *ShellConfig) error {
|
||||
t := template.New("envConfig")
|
||||
tmpl, err := t.Parse(envTmpl)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return tmpl.Execute(os.Stdout, shellCfg)
|
||||
}
|
||||
|
||||
func getShell(userShell string) (string, error) {
|
||||
if userShell != "" {
|
||||
return userShell, nil
|
||||
}
|
||||
return shell.Detect()
|
||||
}
|
||||
|
||||
func findNoProxyFromEnv() (string, string) {
|
||||
// first check for an existing lower case no_proxy var
|
||||
noProxyVar := "no_proxy"
|
||||
noProxyValue := os.Getenv("no_proxy")
|
||||
|
||||
// otherwise default to allcaps HTTP_PROXY
|
||||
if noProxyValue == "" {
|
||||
noProxyVar = "NO_PROXY"
|
||||
noProxyValue = os.Getenv("NO_PROXY")
|
||||
}
|
||||
return noProxyVar, noProxyValue
|
||||
}
|
||||
|
||||
type UsageHintGenerator interface {
|
||||
GenerateUsageHint(string, []string) string
|
||||
}
|
||||
|
||||
type EnvUsageHintGenerator struct{}
|
||||
|
||||
func (g *EnvUsageHintGenerator) GenerateUsageHint(userShell string, args []string) string {
|
||||
cmd := ""
|
||||
comment := "#"
|
||||
|
||||
dockerMachinePath := args[0]
|
||||
if strings.Contains(dockerMachinePath, " ") || strings.Contains(dockerMachinePath, `\`) {
|
||||
args[0] = fmt.Sprintf("\"%s\"", dockerMachinePath)
|
||||
}
|
||||
|
||||
commandLine := strings.Join(args, " ")
|
||||
|
||||
switch userShell {
|
||||
case "fish":
|
||||
cmd = fmt.Sprintf("eval (%s)", commandLine)
|
||||
case "powershell":
|
||||
cmd = fmt.Sprintf("& %s | Invoke-Expression", commandLine)
|
||||
case "cmd":
|
||||
cmd = fmt.Sprintf("\t@FOR /f \"tokens=*\" %%i IN ('%s') DO @%%i", commandLine)
|
||||
comment = "REM"
|
||||
case "emacs":
|
||||
cmd = fmt.Sprintf("(with-temp-buffer (shell-command \"%s\" (current-buffer)) (eval-buffer))", commandLine)
|
||||
comment = ";;"
|
||||
case "tcsh":
|
||||
cmd = fmt.Sprintf("eval `%s`", commandLine)
|
||||
comment = ":"
|
||||
default:
|
||||
cmd = fmt.Sprintf("eval $(%s)", commandLine)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s Run this command to configure your shell: \n%s %s\n", comment, comment, cmd)
|
||||
}
|
||||
|
|
@ -0,0 +1,600 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/machine/commands/commandstest"
|
||||
"github.com/docker/machine/commands/mcndirs"
|
||||
"github.com/docker/machine/drivers/fakedriver"
|
||||
"github.com/docker/machine/libmachine"
|
||||
"github.com/docker/machine/libmachine/auth"
|
||||
"github.com/docker/machine/libmachine/check"
|
||||
"github.com/docker/machine/libmachine/host"
|
||||
"github.com/docker/machine/libmachine/libmachinetest"
|
||||
"github.com/docker/machine/libmachine/state"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type FakeConnChecker struct {
|
||||
DockerHost string
|
||||
AuthOptions *auth.Options
|
||||
Err error
|
||||
}
|
||||
|
||||
func (fcc *FakeConnChecker) Check(_ *host.Host, _ bool) (string, *auth.Options, error) {
|
||||
return fcc.DockerHost, fcc.AuthOptions, fcc.Err
|
||||
}
|
||||
|
||||
type SimpleUsageHintGenerator struct {
|
||||
Hint string
|
||||
}
|
||||
|
||||
func (suhg *SimpleUsageHintGenerator) GenerateUsageHint(_ string, _ []string) string {
|
||||
return suhg.Hint
|
||||
}
|
||||
|
||||
func TestHints(t *testing.T) {
|
||||
var tests = []struct {
|
||||
userShell string
|
||||
commandLine []string
|
||||
expectedHints string
|
||||
}{
|
||||
{"", []string{"machine", "env", "default"}, "# Run this command to configure your shell: \n# eval $(machine env default)\n"},
|
||||
{"", []string{"machine", "env", "--no-proxy", "default"}, "# Run this command to configure your shell: \n# eval $(machine env --no-proxy default)\n"},
|
||||
{"", []string{"machine", "env", "--swarm", "default"}, "# Run this command to configure your shell: \n# eval $(machine env --swarm default)\n"},
|
||||
{"", []string{"machine", "env", "--no-proxy", "--swarm", "default"}, "# Run this command to configure your shell: \n# eval $(machine env --no-proxy --swarm default)\n"},
|
||||
{"", []string{"machine", "env", "--unset"}, "# Run this command to configure your shell: \n# eval $(machine env --unset)\n"},
|
||||
{"", []string{`C:\\Program Files\docker-machine.exe`, "env", "default"}, "# Run this command to configure your shell: \n# eval $(\"C:\\\\Program Files\\docker-machine.exe\" env default)\n"},
|
||||
{"", []string{`C:\\Me\docker-machine.exe`, "env", "default"}, "# Run this command to configure your shell: \n# eval $(\"C:\\\\Me\\docker-machine.exe\" env default)\n"},
|
||||
|
||||
{"fish", []string{"./machine", "env", "--shell=fish", "default"}, "# Run this command to configure your shell: \n# eval (./machine env --shell=fish default)\n"},
|
||||
{"fish", []string{"./machine", "env", "--shell=fish", "--no-proxy", "default"}, "# Run this command to configure your shell: \n# eval (./machine env --shell=fish --no-proxy default)\n"},
|
||||
{"fish", []string{"./machine", "env", "--shell=fish", "--swarm", "default"}, "# Run this command to configure your shell: \n# eval (./machine env --shell=fish --swarm default)\n"},
|
||||
{"fish", []string{"./machine", "env", "--shell=fish", "--no-proxy", "--swarm", "default"}, "# Run this command to configure your shell: \n# eval (./machine env --shell=fish --no-proxy --swarm default)\n"},
|
||||
{"fish", []string{"./machine", "env", "--shell=fish", "--unset"}, "# Run this command to configure your shell: \n# eval (./machine env --shell=fish --unset)\n"},
|
||||
|
||||
{"powershell", []string{"./machine", "env", "--shell=powershell", "default"}, "# Run this command to configure your shell: \n# & ./machine env --shell=powershell default | Invoke-Expression\n"},
|
||||
{"powershell", []string{"./machine", "env", "--shell=powershell", "--no-proxy", "default"}, "# Run this command to configure your shell: \n# & ./machine env --shell=powershell --no-proxy default | Invoke-Expression\n"},
|
||||
{"powershell", []string{"./machine", "env", "--shell=powershell", "--swarm", "default"}, "# Run this command to configure your shell: \n# & ./machine env --shell=powershell --swarm default | Invoke-Expression\n"},
|
||||
{"powershell", []string{"./machine", "env", "--shell=powershell", "--no-proxy", "--swarm", "default"}, "# Run this command to configure your shell: \n# & ./machine env --shell=powershell --no-proxy --swarm default | Invoke-Expression\n"},
|
||||
{"powershell", []string{"./machine", "env", "--shell=powershell", "--unset"}, "# Run this command to configure your shell: \n# & ./machine env --shell=powershell --unset | Invoke-Expression\n"},
|
||||
{"powershell", []string{"./machine", "env", "--shell=powershell", "--unset"}, "# Run this command to configure your shell: \n# & ./machine env --shell=powershell --unset | Invoke-Expression\n"},
|
||||
{"powershell", []string{`C:\\Program Files\docker-machine.exe`, "env", "--shell=powershell", "default"}, "# Run this command to configure your shell: \n# & \"C:\\\\Program Files\\docker-machine.exe\" env --shell=powershell default | Invoke-Expression\n"},
|
||||
{"powershell", []string{`C:\\Me\docker-machine.exe`, "env", "--shell=powershell", "default"}, "# Run this command to configure your shell: \n# & \"C:\\\\Me\\docker-machine.exe\" env --shell=powershell default | Invoke-Expression\n"},
|
||||
|
||||
{"cmd", []string{"./machine", "env", "--shell=cmd", "default"}, "REM Run this command to configure your shell: \nREM \t@FOR /f \"tokens=*\" %i IN ('./machine env --shell=cmd default') DO @%i\n"},
|
||||
{"cmd", []string{"./machine", "env", "--shell=cmd", "--no-proxy", "default"}, "REM Run this command to configure your shell: \nREM \t@FOR /f \"tokens=*\" %i IN ('./machine env --shell=cmd --no-proxy default') DO @%i\n"},
|
||||
{"cmd", []string{"./machine", "env", "--shell=cmd", "--swarm", "default"}, "REM Run this command to configure your shell: \nREM \t@FOR /f \"tokens=*\" %i IN ('./machine env --shell=cmd --swarm default') DO @%i\n"},
|
||||
{"cmd", []string{"./machine", "env", "--shell=cmd", "--no-proxy", "--swarm", "default"}, "REM Run this command to configure your shell: \nREM \t@FOR /f \"tokens=*\" %i IN ('./machine env --shell=cmd --no-proxy --swarm default') DO @%i\n"},
|
||||
{"cmd", []string{"./machine", "env", "--shell=cmd", "--unset"}, "REM Run this command to configure your shell: \nREM \t@FOR /f \"tokens=*\" %i IN ('./machine env --shell=cmd --unset') DO @%i\n"},
|
||||
{"cmd", []string{`C:\\Program Files\docker-machine.exe`, "env", "--shell=cmd", "default"}, "REM Run this command to configure your shell: \nREM \t@FOR /f \"tokens=*\" %i IN ('\"C:\\\\Program Files\\docker-machine.exe\" env --shell=cmd default') DO @%i\n"},
|
||||
{"cmd", []string{`C:\\Me\docker-machine.exe`, "env", "--shell=cmd", "default"}, "REM Run this command to configure your shell: \nREM \t@FOR /f \"tokens=*\" %i IN ('\"C:\\\\Me\\docker-machine.exe\" env --shell=cmd default') DO @%i\n"},
|
||||
|
||||
{"emacs", []string{"./machine", "env", "--shell=emacs", "default"}, ";; Run this command to configure your shell: \n;; (with-temp-buffer (shell-command \"./machine env --shell=emacs default\" (current-buffer)) (eval-buffer))\n"},
|
||||
{"emacs", []string{"./machine", "env", "--shell=emacs", "--no-proxy", "default"}, ";; Run this command to configure your shell: \n;; (with-temp-buffer (shell-command \"./machine env --shell=emacs --no-proxy default\" (current-buffer)) (eval-buffer))\n"},
|
||||
{"emacs", []string{"./machine", "env", "--shell=emacs", "--swarm", "default"}, ";; Run this command to configure your shell: \n;; (with-temp-buffer (shell-command \"./machine env --shell=emacs --swarm default\" (current-buffer)) (eval-buffer))\n"},
|
||||
{"emacs", []string{"./machine", "env", "--shell=emacs", "--no-proxy", "--swarm", "default"}, ";; Run this command to configure your shell: \n;; (with-temp-buffer (shell-command \"./machine env --shell=emacs --no-proxy --swarm default\" (current-buffer)) (eval-buffer))\n"},
|
||||
{"emacs", []string{"./machine", "env", "--shell=emacs", "--unset"}, ";; Run this command to configure your shell: \n;; (with-temp-buffer (shell-command \"./machine env --shell=emacs --unset\" (current-buffer)) (eval-buffer))\n"},
|
||||
|
||||
{"tcsh", []string{"./machine", "env", "--shell=tcsh", "default"}, ": Run this command to configure your shell: \n: eval `./machine env --shell=tcsh default`\n"},
|
||||
{"tcsh", []string{"./machine", "env", "--shell=tcsh", "--no-proxy", "default"}, ": Run this command to configure your shell: \n: eval `./machine env --shell=tcsh --no-proxy default`\n"},
|
||||
{"tcsh", []string{"./machine", "env", "--shell=tcsh", "--swarm", "default"}, ": Run this command to configure your shell: \n: eval `./machine env --shell=tcsh --swarm default`\n"},
|
||||
{"tcsh", []string{"./machine", "env", "--shell=tcsh", "--no-proxy", "--swarm", "default"}, ": Run this command to configure your shell: \n: eval `./machine env --shell=tcsh --no-proxy --swarm default`\n"},
|
||||
{"tcsh", []string{"./machine", "env", "--shell=tcsh", "--unset"}, ": Run this command to configure your shell: \n: eval `./machine env --shell=tcsh --unset`\n"},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
hints := defaultUsageHinter.GenerateUsageHint(test.userShell, test.commandLine)
|
||||
assert.Equal(t, test.expectedHints, hints)
|
||||
}
|
||||
}
|
||||
|
||||
func revertUsageHinter(uhg UsageHintGenerator) {
|
||||
defaultUsageHinter = uhg
|
||||
}
|
||||
|
||||
func TestShellCfgSet(t *testing.T) {
|
||||
const (
|
||||
usageHint = "This is a usage hint"
|
||||
)
|
||||
|
||||
// TODO: This should be embedded in some kind of wrapper struct for all
|
||||
// these `env` operations.
|
||||
defer revertUsageHinter(defaultUsageHinter)
|
||||
defaultUsageHinter = &SimpleUsageHintGenerator{usageHint}
|
||||
|
||||
var tests = []struct {
|
||||
description string
|
||||
commandLine CommandLine
|
||||
api libmachine.API
|
||||
connChecker check.ConnChecker
|
||||
noProxyVar string
|
||||
noProxyValue string
|
||||
expectedShellCfg *ShellConfig
|
||||
expectedErr error
|
||||
}{
|
||||
{
|
||||
description: "no host name specified",
|
||||
api: &libmachinetest.FakeAPI{
|
||||
Hosts: []*host.Host{},
|
||||
},
|
||||
commandLine: &commandstest.FakeCommandLine{
|
||||
CliArgs: nil,
|
||||
},
|
||||
expectedShellCfg: nil,
|
||||
expectedErr: ErrNoDefault,
|
||||
},
|
||||
{
|
||||
description: "bash shell set happy path without any flags set",
|
||||
commandLine: &commandstest.FakeCommandLine{
|
||||
CliArgs: []string{"quux"},
|
||||
LocalFlags: &commandstest.FakeFlagger{
|
||||
Data: map[string]interface{}{
|
||||
"shell": "bash",
|
||||
"swarm": false,
|
||||
"no-proxy": false,
|
||||
},
|
||||
},
|
||||
},
|
||||
api: &libmachinetest.FakeAPI{
|
||||
Hosts: []*host.Host{
|
||||
{
|
||||
Name: "quux",
|
||||
},
|
||||
},
|
||||
},
|
||||
connChecker: &FakeConnChecker{
|
||||
DockerHost: "tcp://1.2.3.4:2376",
|
||||
AuthOptions: nil,
|
||||
Err: nil,
|
||||
},
|
||||
expectedShellCfg: &ShellConfig{
|
||||
Prefix: "export ",
|
||||
Delimiter: "=\"",
|
||||
Suffix: "\"\n",
|
||||
DockerCertPath: filepath.Join(mcndirs.GetMachineDir(), "quux"),
|
||||
DockerHost: "tcp://1.2.3.4:2376",
|
||||
DockerTLSVerify: "1",
|
||||
UsageHint: usageHint,
|
||||
MachineName: "quux",
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
description: "bash shell set happy path with 'default' vm",
|
||||
commandLine: &commandstest.FakeCommandLine{
|
||||
CliArgs: []string{},
|
||||
LocalFlags: &commandstest.FakeFlagger{
|
||||
Data: map[string]interface{}{
|
||||
"shell": "bash",
|
||||
"swarm": false,
|
||||
"no-proxy": false,
|
||||
},
|
||||
},
|
||||
},
|
||||
api: &libmachinetest.FakeAPI{
|
||||
Hosts: []*host.Host{
|
||||
{
|
||||
Name: defaultMachineName,
|
||||
},
|
||||
},
|
||||
},
|
||||
connChecker: &FakeConnChecker{
|
||||
DockerHost: "tcp://1.2.3.4:2376",
|
||||
AuthOptions: nil,
|
||||
Err: nil,
|
||||
},
|
||||
expectedShellCfg: &ShellConfig{
|
||||
Prefix: "export ",
|
||||
Delimiter: "=\"",
|
||||
Suffix: "\"\n",
|
||||
DockerCertPath: filepath.Join(mcndirs.GetMachineDir(), defaultMachineName),
|
||||
DockerHost: "tcp://1.2.3.4:2376",
|
||||
DockerTLSVerify: "1",
|
||||
UsageHint: usageHint,
|
||||
MachineName: defaultMachineName,
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
description: "fish shell set happy path",
|
||||
commandLine: &commandstest.FakeCommandLine{
|
||||
CliArgs: []string{"quux"},
|
||||
LocalFlags: &commandstest.FakeFlagger{
|
||||
Data: map[string]interface{}{
|
||||
"shell": "fish",
|
||||
"swarm": false,
|
||||
"no-proxy": false,
|
||||
},
|
||||
},
|
||||
},
|
||||
api: &libmachinetest.FakeAPI{
|
||||
Hosts: []*host.Host{
|
||||
{
|
||||
Name: "quux",
|
||||
},
|
||||
},
|
||||
},
|
||||
connChecker: &FakeConnChecker{
|
||||
DockerHost: "tcp://1.2.3.4:2376",
|
||||
AuthOptions: nil,
|
||||
Err: nil,
|
||||
},
|
||||
expectedShellCfg: &ShellConfig{
|
||||
Prefix: "set -gx ",
|
||||
Suffix: "\";\n",
|
||||
Delimiter: " \"",
|
||||
DockerCertPath: filepath.Join(mcndirs.GetMachineDir(), "quux"),
|
||||
DockerHost: "tcp://1.2.3.4:2376",
|
||||
DockerTLSVerify: "1",
|
||||
UsageHint: usageHint,
|
||||
MachineName: "quux",
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
description: "powershell set happy path",
|
||||
commandLine: &commandstest.FakeCommandLine{
|
||||
CliArgs: []string{"quux"},
|
||||
LocalFlags: &commandstest.FakeFlagger{
|
||||
Data: map[string]interface{}{
|
||||
"shell": "powershell",
|
||||
"swarm": false,
|
||||
"no-proxy": false,
|
||||
},
|
||||
},
|
||||
},
|
||||
api: &libmachinetest.FakeAPI{
|
||||
Hosts: []*host.Host{
|
||||
{
|
||||
Name: "quux",
|
||||
},
|
||||
},
|
||||
},
|
||||
connChecker: &FakeConnChecker{
|
||||
DockerHost: "tcp://1.2.3.4:2376",
|
||||
AuthOptions: nil,
|
||||
Err: nil,
|
||||
},
|
||||
expectedShellCfg: &ShellConfig{
|
||||
Prefix: "$Env:",
|
||||
Suffix: "\"\n",
|
||||
Delimiter: " = \"",
|
||||
DockerCertPath: filepath.Join(mcndirs.GetMachineDir(), "quux"),
|
||||
DockerHost: "tcp://1.2.3.4:2376",
|
||||
DockerTLSVerify: "1",
|
||||
UsageHint: usageHint,
|
||||
MachineName: "quux",
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
description: "emacs set happy path",
|
||||
commandLine: &commandstest.FakeCommandLine{
|
||||
CliArgs: []string{"quux"},
|
||||
LocalFlags: &commandstest.FakeFlagger{
|
||||
Data: map[string]interface{}{
|
||||
"shell": "emacs",
|
||||
"swarm": false,
|
||||
"no-proxy": false,
|
||||
},
|
||||
},
|
||||
},
|
||||
api: &libmachinetest.FakeAPI{
|
||||
Hosts: []*host.Host{
|
||||
{
|
||||
Name: "quux",
|
||||
},
|
||||
},
|
||||
},
|
||||
connChecker: &FakeConnChecker{
|
||||
DockerHost: "tcp://1.2.3.4:2376",
|
||||
AuthOptions: nil,
|
||||
Err: nil,
|
||||
},
|
||||
expectedShellCfg: &ShellConfig{
|
||||
Prefix: "(setenv \"",
|
||||
Suffix: "\")\n",
|
||||
Delimiter: "\" \"",
|
||||
DockerCertPath: filepath.Join(mcndirs.GetMachineDir(), "quux"),
|
||||
DockerHost: "tcp://1.2.3.4:2376",
|
||||
DockerTLSVerify: "1",
|
||||
UsageHint: usageHint,
|
||||
MachineName: "quux",
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
description: "cmd.exe happy path",
|
||||
commandLine: &commandstest.FakeCommandLine{
|
||||
CliArgs: []string{"quux"},
|
||||
LocalFlags: &commandstest.FakeFlagger{
|
||||
Data: map[string]interface{}{
|
||||
"shell": "cmd",
|
||||
"swarm": false,
|
||||
"no-proxy": false,
|
||||
},
|
||||
},
|
||||
},
|
||||
api: &libmachinetest.FakeAPI{
|
||||
Hosts: []*host.Host{
|
||||
{
|
||||
Name: "quux",
|
||||
},
|
||||
},
|
||||
},
|
||||
connChecker: &FakeConnChecker{
|
||||
DockerHost: "tcp://1.2.3.4:2376",
|
||||
AuthOptions: nil,
|
||||
Err: nil,
|
||||
},
|
||||
expectedShellCfg: &ShellConfig{
|
||||
Prefix: "SET ",
|
||||
Suffix: "\n",
|
||||
Delimiter: "=",
|
||||
DockerCertPath: filepath.Join(mcndirs.GetMachineDir(), "quux"),
|
||||
DockerHost: "tcp://1.2.3.4:2376",
|
||||
DockerTLSVerify: "1",
|
||||
UsageHint: usageHint,
|
||||
MachineName: "quux",
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
description: "bash shell set happy path with --no-proxy flag; no existing environment variable set",
|
||||
commandLine: &commandstest.FakeCommandLine{
|
||||
CliArgs: []string{"quux"},
|
||||
LocalFlags: &commandstest.FakeFlagger{
|
||||
Data: map[string]interface{}{
|
||||
"shell": "bash",
|
||||
"swarm": false,
|
||||
"no-proxy": true,
|
||||
},
|
||||
},
|
||||
},
|
||||
api: &libmachinetest.FakeAPI{
|
||||
Hosts: []*host.Host{
|
||||
{
|
||||
Name: "quux",
|
||||
Driver: &fakedriver.Driver{
|
||||
MockState: state.Running,
|
||||
MockIP: "1.2.3.4",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
connChecker: &FakeConnChecker{
|
||||
DockerHost: "tcp://1.2.3.4:2376",
|
||||
AuthOptions: nil,
|
||||
Err: nil,
|
||||
},
|
||||
expectedShellCfg: &ShellConfig{
|
||||
Prefix: "export ",
|
||||
Delimiter: "=\"",
|
||||
Suffix: "\"\n",
|
||||
DockerCertPath: filepath.Join(mcndirs.GetMachineDir(), "quux"),
|
||||
DockerHost: "tcp://1.2.3.4:2376",
|
||||
DockerTLSVerify: "1",
|
||||
UsageHint: usageHint,
|
||||
NoProxyVar: "NO_PROXY",
|
||||
NoProxyValue: "1.2.3.4", // From FakeDriver
|
||||
MachineName: "quux",
|
||||
},
|
||||
noProxyVar: "NO_PROXY",
|
||||
noProxyValue: "",
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
description: "bash shell set happy path with --no-proxy flag; existing environment variable _is_ set",
|
||||
commandLine: &commandstest.FakeCommandLine{
|
||||
CliArgs: []string{"quux"},
|
||||
LocalFlags: &commandstest.FakeFlagger{
|
||||
Data: map[string]interface{}{
|
||||
"shell": "bash",
|
||||
"swarm": false,
|
||||
"no-proxy": true,
|
||||
},
|
||||
},
|
||||
},
|
||||
api: &libmachinetest.FakeAPI{
|
||||
Hosts: []*host.Host{
|
||||
{
|
||||
Name: "quux",
|
||||
Driver: &fakedriver.Driver{
|
||||
MockState: state.Running,
|
||||
MockIP: "1.2.3.4",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
connChecker: &FakeConnChecker{
|
||||
DockerHost: "tcp://1.2.3.4:2376",
|
||||
AuthOptions: nil,
|
||||
Err: nil,
|
||||
},
|
||||
expectedShellCfg: &ShellConfig{
|
||||
Prefix: "export ",
|
||||
Delimiter: "=\"",
|
||||
Suffix: "\"\n",
|
||||
DockerCertPath: filepath.Join(mcndirs.GetMachineDir(), "quux"),
|
||||
DockerHost: "tcp://1.2.3.4:2376",
|
||||
DockerTLSVerify: "1",
|
||||
UsageHint: usageHint,
|
||||
NoProxyVar: "no_proxy",
|
||||
NoProxyValue: "192.168.59.1,1.2.3.4", // From FakeDriver
|
||||
MachineName: "quux",
|
||||
},
|
||||
noProxyVar: "no_proxy",
|
||||
noProxyValue: "192.168.59.1",
|
||||
expectedErr: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
// TODO: Ideally this should not hit the environment at all but
|
||||
// rather should go through an interface.
|
||||
os.Setenv(test.noProxyVar, test.noProxyValue)
|
||||
|
||||
t.Log(test.description)
|
||||
|
||||
check.DefaultConnChecker = test.connChecker
|
||||
shellCfg, err := shellCfgSet(test.commandLine, test.api)
|
||||
assert.Equal(t, test.expectedShellCfg, shellCfg)
|
||||
assert.Equal(t, test.expectedErr, err)
|
||||
|
||||
os.Unsetenv(test.noProxyVar)
|
||||
}
|
||||
}
|
||||
|
||||
func TestShellCfgUnset(t *testing.T) {
|
||||
const (
|
||||
usageHint = "This is the unset usage hint"
|
||||
)
|
||||
|
||||
defer revertUsageHinter(defaultUsageHinter)
|
||||
defaultUsageHinter = &SimpleUsageHintGenerator{usageHint}
|
||||
|
||||
var tests = []struct {
|
||||
description string
|
||||
commandLine CommandLine
|
||||
api libmachine.API
|
||||
connChecker check.ConnChecker
|
||||
noProxyVar string
|
||||
noProxyValue string
|
||||
expectedShellCfg *ShellConfig
|
||||
expectedErr error
|
||||
}{
|
||||
{
|
||||
description: "more than expected args passed in",
|
||||
commandLine: &commandstest.FakeCommandLine{
|
||||
CliArgs: []string{"foo", "bar"},
|
||||
},
|
||||
expectedShellCfg: nil,
|
||||
expectedErr: errImproperUnsetEnvArgs,
|
||||
},
|
||||
{
|
||||
description: "bash shell unset happy path without any flags set",
|
||||
commandLine: &commandstest.FakeCommandLine{
|
||||
CliArgs: nil,
|
||||
LocalFlags: &commandstest.FakeFlagger{
|
||||
Data: map[string]interface{}{
|
||||
"shell": "bash",
|
||||
"swarm": false,
|
||||
"no-proxy": false,
|
||||
},
|
||||
},
|
||||
},
|
||||
api: &libmachinetest.FakeAPI{},
|
||||
connChecker: &FakeConnChecker{
|
||||
DockerHost: "tcp://1.2.3.4:2376",
|
||||
AuthOptions: nil,
|
||||
Err: nil,
|
||||
},
|
||||
expectedShellCfg: &ShellConfig{
|
||||
Prefix: "unset ",
|
||||
Suffix: "\n",
|
||||
Delimiter: "",
|
||||
UsageHint: usageHint,
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
description: "fish shell unset happy path",
|
||||
commandLine: &commandstest.FakeCommandLine{
|
||||
CliArgs: nil,
|
||||
LocalFlags: &commandstest.FakeFlagger{
|
||||
Data: map[string]interface{}{
|
||||
"shell": "fish",
|
||||
"swarm": false,
|
||||
"no-proxy": false,
|
||||
},
|
||||
},
|
||||
},
|
||||
api: &libmachinetest.FakeAPI{},
|
||||
connChecker: &FakeConnChecker{
|
||||
DockerHost: "tcp://1.2.3.4:2376",
|
||||
AuthOptions: nil,
|
||||
Err: nil,
|
||||
},
|
||||
expectedShellCfg: &ShellConfig{
|
||||
Prefix: "set -e ",
|
||||
Suffix: ";\n",
|
||||
Delimiter: "",
|
||||
UsageHint: usageHint,
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
description: "powershell unset happy path",
|
||||
commandLine: &commandstest.FakeCommandLine{
|
||||
CliArgs: nil,
|
||||
LocalFlags: &commandstest.FakeFlagger{
|
||||
Data: map[string]interface{}{
|
||||
"shell": "powershell",
|
||||
"swarm": false,
|
||||
"no-proxy": false,
|
||||
},
|
||||
},
|
||||
},
|
||||
api: &libmachinetest.FakeAPI{},
|
||||
connChecker: &FakeConnChecker{
|
||||
DockerHost: "tcp://1.2.3.4:2376",
|
||||
AuthOptions: nil,
|
||||
Err: nil,
|
||||
},
|
||||
expectedShellCfg: &ShellConfig{
|
||||
Prefix: `Remove-Item Env:\\`,
|
||||
Suffix: "\n",
|
||||
Delimiter: "",
|
||||
UsageHint: usageHint,
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
description: "cmd.exe unset happy path",
|
||||
commandLine: &commandstest.FakeCommandLine{
|
||||
CliArgs: nil,
|
||||
LocalFlags: &commandstest.FakeFlagger{
|
||||
Data: map[string]interface{}{
|
||||
"shell": "cmd",
|
||||
"swarm": false,
|
||||
"no-proxy": false,
|
||||
},
|
||||
},
|
||||
},
|
||||
api: &libmachinetest.FakeAPI{},
|
||||
connChecker: &FakeConnChecker{
|
||||
DockerHost: "tcp://1.2.3.4:2376",
|
||||
AuthOptions: nil,
|
||||
Err: nil,
|
||||
},
|
||||
expectedShellCfg: &ShellConfig{
|
||||
Prefix: "SET ",
|
||||
Suffix: "\n",
|
||||
Delimiter: "=",
|
||||
UsageHint: usageHint,
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
// TODO: There is kind of a funny bug (feature?) I discovered
|
||||
// reasoning about unset() where if there was a NO_PROXY value
|
||||
// set _before_ the original docker-machine env, it won't be
|
||||
// restored (NO_PROXY won't be unset at all, it will stay the
|
||||
// same). We should define expected behavior in this case.
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
os.Setenv(test.noProxyVar, test.noProxyValue)
|
||||
|
||||
t.Log(test.description)
|
||||
|
||||
check.DefaultConnChecker = test.connChecker
|
||||
shellCfg, err := shellCfgUnset(test.commandLine, test.api)
|
||||
assert.Equal(t, test.expectedShellCfg, shellCfg)
|
||||
assert.Equal(t, test.expectedErr, err)
|
||||
|
||||
os.Setenv(test.noProxyVar, "")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package commands
|
||||
|
||||
import "github.com/codegangsta/cli"
|
||||
|
||||
type ByFlagName []cli.Flag
|
||||
|
||||
func (flags ByFlagName) Len() int {
|
||||
return len(flags)
|
||||
}
|
||||
|
||||
func (flags ByFlagName) Swap(i, j int) {
|
||||
flags[i], flags[j] = flags[j], flags[i]
|
||||
}
|
||||
|
||||
func (flags ByFlagName) Less(i, j int) bool {
|
||||
return flags[i].String() < flags[j].String()
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"text/template"
|
||||
|
||||
"github.com/docker/machine/libmachine"
|
||||
)
|
||||
|
||||
var funcMap = template.FuncMap{
|
||||
"json": func(v interface{}) string {
|
||||
a, _ := json.Marshal(v)
|
||||
return string(a)
|
||||
},
|
||||
"prettyjson": func(v interface{}) string {
|
||||
a, _ := json.MarshalIndent(v, "", " ")
|
||||
return string(a)
|
||||
},
|
||||
}
|
||||
|
||||
func cmdInspect(c CommandLine, api libmachine.API) error {
|
||||
if len(c.Args()) > 1 {
|
||||
c.ShowHelp()
|
||||
return ErrExpectedOneMachine
|
||||
}
|
||||
|
||||
target, err := targetHost(c, api)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
host, err := api.Load(target)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tmplString := c.String("format")
|
||||
if tmplString != "" {
|
||||
var tmpl *template.Template
|
||||
var err error
|
||||
if tmpl, err = template.New("").Funcs(funcMap).Parse(tmplString); err != nil {
|
||||
return fmt.Errorf("Template parsing error: %v\n", err)
|
||||
}
|
||||
|
||||
jsonHost, err := json.Marshal(host)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
obj := make(map[string]interface{})
|
||||
if err := json.Unmarshal(jsonHost, &obj); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := tmpl.Execute(os.Stdout, obj); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
os.Stdout.Write([]byte{'\n'})
|
||||
} else {
|
||||
prettyJSON, err := json.MarshalIndent(host, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println(string(prettyJSON))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/docker/machine/commands/commandstest"
|
||||
"github.com/docker/machine/libmachine"
|
||||
"github.com/docker/machine/libmachine/libmachinetest"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCmdInspect(t *testing.T) {
|
||||
testCases := []struct {
|
||||
commandLine CommandLine
|
||||
api libmachine.API
|
||||
expectedErr error
|
||||
}{
|
||||
{
|
||||
commandLine: &commandstest.FakeCommandLine{
|
||||
CliArgs: []string{"foo", "bar"},
|
||||
},
|
||||
api: &libmachinetest.FakeAPI{},
|
||||
expectedErr: ErrExpectedOneMachine,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
err := cmdInspect(tc.commandLine, tc.api)
|
||||
assert.Equal(t, tc.expectedErr, err)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package commands
|
||||
|
||||
import "github.com/docker/machine/libmachine"
|
||||
|
||||
func cmdIP(c CommandLine, api libmachine.API) error {
|
||||
return runAction("ip", c, api)
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/docker/machine/commands/commandstest"
|
||||
"github.com/docker/machine/drivers/fakedriver"
|
||||
"github.com/docker/machine/libmachine"
|
||||
"github.com/docker/machine/libmachine/host"
|
||||
"github.com/docker/machine/libmachine/libmachinetest"
|
||||
"github.com/docker/machine/libmachine/state"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCmdIPMissingMachineName(t *testing.T) {
|
||||
commandLine := &commandstest.FakeCommandLine{}
|
||||
api := &libmachinetest.FakeAPI{}
|
||||
|
||||
err := cmdURL(commandLine, api)
|
||||
|
||||
assert.Equal(t, err, ErrNoDefault)
|
||||
}
|
||||
|
||||
func TestCmdIP(t *testing.T) {
|
||||
testCases := []struct {
|
||||
commandLine CommandLine
|
||||
api libmachine.API
|
||||
expectedErr error
|
||||
expectedOut string
|
||||
}{
|
||||
{
|
||||
commandLine: &commandstest.FakeCommandLine{
|
||||
CliArgs: []string{"machine"},
|
||||
},
|
||||
api: &libmachinetest.FakeAPI{
|
||||
Hosts: []*host.Host{
|
||||
{
|
||||
Name: "machine",
|
||||
Driver: &fakedriver.Driver{
|
||||
MockState: state.Running,
|
||||
MockIP: "1.2.3.4",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErr: nil,
|
||||
expectedOut: "1.2.3.4\n",
|
||||
},
|
||||
{
|
||||
commandLine: &commandstest.FakeCommandLine{
|
||||
CliArgs: []string{},
|
||||
},
|
||||
api: &libmachinetest.FakeAPI{
|
||||
Hosts: []*host.Host{
|
||||
{
|
||||
Name: "default",
|
||||
Driver: &fakedriver.Driver{
|
||||
MockState: state.Running,
|
||||
MockIP: "1.2.3.4",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErr: nil,
|
||||
expectedOut: "1.2.3.4\n",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
stdoutGetter := commandstest.NewStdoutGetter()
|
||||
|
||||
err := cmdIP(tc.commandLine, tc.api)
|
||||
|
||||
assert.Equal(t, tc.expectedErr, err)
|
||||
assert.Equal(t, tc.expectedOut, stdoutGetter.Output())
|
||||
|
||||
stdoutGetter.Stop()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package commands
|
||||
|
||||
import "github.com/docker/machine/libmachine"
|
||||
|
||||
func cmdKill(c CommandLine, api libmachine.API) error {
|
||||
return runAction("kill", c, api)
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/docker/machine/commands/commandstest"
|
||||
"github.com/docker/machine/drivers/fakedriver"
|
||||
"github.com/docker/machine/libmachine/host"
|
||||
"github.com/docker/machine/libmachine/libmachinetest"
|
||||
"github.com/docker/machine/libmachine/state"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCmdKillMissingMachineName(t *testing.T) {
|
||||
commandLine := &commandstest.FakeCommandLine{}
|
||||
api := &libmachinetest.FakeAPI{}
|
||||
|
||||
err := cmdKill(commandLine, api)
|
||||
|
||||
assert.Equal(t, ErrNoDefault, err)
|
||||
}
|
||||
|
||||
func TestCmdKill(t *testing.T) {
|
||||
commandLine := &commandstest.FakeCommandLine{
|
||||
CliArgs: []string{"machineToKill1", "machineToKill2"},
|
||||
}
|
||||
api := &libmachinetest.FakeAPI{
|
||||
Hosts: []*host.Host{
|
||||
{
|
||||
Name: "machineToKill1",
|
||||
Driver: &fakedriver.Driver{
|
||||
MockState: state.Running,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "machineToKill2",
|
||||
Driver: &fakedriver.Driver{
|
||||
MockState: state.Running,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "machine",
|
||||
Driver: &fakedriver.Driver{
|
||||
MockState: state.Running,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := cmdKill(commandLine, api)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, state.Stopped, libmachinetest.State(api, "machineToKill1"))
|
||||
assert.Equal(t, state.Stopped, libmachinetest.State(api, "machineToKill2"))
|
||||
assert.Equal(t, state.Running, libmachinetest.State(api, "machine"))
|
||||
}
|
||||
|
|
@ -0,0 +1,506 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"io"
|
||||
|
||||
"github.com/docker/machine/libmachine"
|
||||
"github.com/docker/machine/libmachine/drivers"
|
||||
"github.com/docker/machine/libmachine/engine"
|
||||
"github.com/docker/machine/libmachine/host"
|
||||
"github.com/docker/machine/libmachine/log"
|
||||
"github.com/docker/machine/libmachine/mcndockerclient"
|
||||
"github.com/docker/machine/libmachine/persist"
|
||||
"github.com/docker/machine/libmachine/state"
|
||||
"github.com/docker/machine/libmachine/swarm"
|
||||
"github.com/skarademir/naturalsort"
|
||||
)
|
||||
|
||||
const (
|
||||
lsDefaultTimeout = 10
|
||||
tableFormatKey = "table"
|
||||
lsDefaultFormat = "table {{ .Name }}\t{{ .Active }}\t{{ .DriverName}}\t{{ .State }}\t{{ .URL }}\t{{ .Swarm }}\t{{ .DockerVersion }}\t{{ .Error}}"
|
||||
)
|
||||
|
||||
var (
|
||||
headers = map[string]string{
|
||||
"Name": "NAME",
|
||||
"Active": "ACTIVE",
|
||||
"ActiveHost": "ACTIVE_HOST",
|
||||
"ActiveSwarm": "ACTIVE_SWARM",
|
||||
"DriverName": "DRIVER",
|
||||
"State": "STATE",
|
||||
"URL": "URL",
|
||||
"SwarmOptions": "SWARM_OPTIONS",
|
||||
"Swarm": "SWARM",
|
||||
"EngineOptions": "ENGINE_OPTIONS",
|
||||
"Error": "ERRORS",
|
||||
"DockerVersion": "DOCKER",
|
||||
"ResponseTime": "RESPONSE",
|
||||
}
|
||||
)
|
||||
|
||||
type HostListItem struct {
|
||||
Name string
|
||||
Active string
|
||||
ActiveHost bool
|
||||
ActiveSwarm bool
|
||||
DriverName string
|
||||
State state.State
|
||||
URL string
|
||||
SwarmOptions *swarm.Options
|
||||
Swarm string
|
||||
EngineOptions *engine.Options
|
||||
Error string
|
||||
DockerVersion string
|
||||
ResponseTime time.Duration
|
||||
}
|
||||
|
||||
// FilterOptions -
|
||||
type FilterOptions struct {
|
||||
SwarmName []string
|
||||
DriverName []string
|
||||
State []string
|
||||
Name []string
|
||||
Labels []string
|
||||
}
|
||||
|
||||
func cmdLs(c CommandLine, api libmachine.API) error {
|
||||
filters, err := parseFilters(c.StringSlice("filter"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hostList, hostInError, err := persist.LoadAllHosts(api)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hostList = filterHosts(hostList, filters)
|
||||
|
||||
// Just print out the names if we're being quiet
|
||||
if c.Bool("quiet") {
|
||||
for _, host := range hostList {
|
||||
fmt.Println(host.Name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
template, table, err := parseFormat(c.String("format"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var w io.Writer
|
||||
if table {
|
||||
tabWriter := tabwriter.NewWriter(os.Stdout, 5, 1, 3, ' ', 0)
|
||||
defer tabWriter.Flush()
|
||||
|
||||
w = tabWriter
|
||||
|
||||
if err := template.Execute(w, headers); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
w = os.Stdout
|
||||
}
|
||||
|
||||
timeout := time.Duration(c.Int("timeout")) * time.Second
|
||||
items := getHostListItems(hostList, hostInError, timeout)
|
||||
|
||||
swarmMasters := make(map[string]string)
|
||||
swarmInfo := make(map[string]string)
|
||||
|
||||
for _, host := range hostList {
|
||||
if host.HostOptions != nil {
|
||||
swarmOptions := host.HostOptions.SwarmOptions
|
||||
if swarmOptions.Master {
|
||||
swarmMasters[swarmOptions.Discovery] = host.Name
|
||||
}
|
||||
|
||||
if swarmOptions.Discovery != "" {
|
||||
swarmInfo[host.Name] = swarmOptions.Discovery
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, item := range items {
|
||||
swarmColumn := ""
|
||||
if item.SwarmOptions != nil && item.SwarmOptions.Discovery != "" {
|
||||
swarmColumn = swarmMasters[item.SwarmOptions.Discovery]
|
||||
if item.SwarmOptions.Master {
|
||||
swarmColumn = fmt.Sprintf("%s (master)", swarmColumn)
|
||||
}
|
||||
}
|
||||
item.Swarm = swarmColumn
|
||||
|
||||
if err := template.Execute(w, item); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseFormat(format string) (*template.Template, bool, error) {
|
||||
table := false
|
||||
finalFormat := format
|
||||
|
||||
if finalFormat == "" {
|
||||
finalFormat = lsDefaultFormat
|
||||
}
|
||||
|
||||
if strings.HasPrefix(finalFormat, tableFormatKey) {
|
||||
table = true
|
||||
finalFormat = finalFormat[len(tableFormatKey):]
|
||||
}
|
||||
|
||||
finalFormat = strings.Trim(finalFormat, " ")
|
||||
r := strings.NewReplacer(`\t`, "\t", `\n`, "\n")
|
||||
finalFormat = r.Replace(finalFormat)
|
||||
|
||||
template, err := template.New("").Parse(finalFormat + "\n")
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
return template, table, nil
|
||||
}
|
||||
|
||||
func parseFilters(filters []string) (FilterOptions, error) {
|
||||
options := FilterOptions{}
|
||||
for _, f := range filters {
|
||||
kv := strings.SplitN(f, "=", 2)
|
||||
if len(kv) != 2 {
|
||||
return options, errors.New("Unsupported filter syntax.")
|
||||
}
|
||||
key, value := strings.ToLower(kv[0]), kv[1]
|
||||
|
||||
switch key {
|
||||
case "swarm":
|
||||
options.SwarmName = append(options.SwarmName, value)
|
||||
case "driver":
|
||||
options.DriverName = append(options.DriverName, value)
|
||||
case "state":
|
||||
options.State = append(options.State, value)
|
||||
case "name":
|
||||
options.Name = append(options.Name, value)
|
||||
case "label":
|
||||
options.Labels = append(options.Labels, value)
|
||||
default:
|
||||
return options, fmt.Errorf("Unsupported filter key '%s'", key)
|
||||
}
|
||||
}
|
||||
return options, nil
|
||||
}
|
||||
|
||||
func filterHosts(hosts []*host.Host, filters FilterOptions) []*host.Host {
|
||||
if len(filters.SwarmName) == 0 &&
|
||||
len(filters.DriverName) == 0 &&
|
||||
len(filters.State) == 0 &&
|
||||
len(filters.Name) == 0 &&
|
||||
len(filters.Labels) == 0 {
|
||||
return hosts
|
||||
}
|
||||
|
||||
filteredHosts := []*host.Host{}
|
||||
swarmMasters := getSwarmMasters(hosts)
|
||||
|
||||
for _, h := range hosts {
|
||||
if filterHost(h, filters, swarmMasters) {
|
||||
filteredHosts = append(filteredHosts, h)
|
||||
}
|
||||
}
|
||||
return filteredHosts
|
||||
}
|
||||
|
||||
func getSwarmMasters(hosts []*host.Host) map[string]string {
|
||||
swarmMasters := make(map[string]string)
|
||||
for _, h := range hosts {
|
||||
if h.HostOptions != nil {
|
||||
swarmOptions := h.HostOptions.SwarmOptions
|
||||
if swarmOptions != nil && swarmOptions.Master {
|
||||
swarmMasters[swarmOptions.Discovery] = h.Name
|
||||
}
|
||||
}
|
||||
}
|
||||
return swarmMasters
|
||||
}
|
||||
|
||||
func filterHost(host *host.Host, filters FilterOptions, swarmMasters map[string]string) bool {
|
||||
swarmMatches := matchesSwarmName(host, filters.SwarmName, swarmMasters)
|
||||
driverMatches := matchesDriverName(host, filters.DriverName)
|
||||
stateMatches := matchesState(host, filters.State)
|
||||
nameMatches := matchesName(host, filters.Name)
|
||||
labelMatches := matchesLabel(host, filters.Labels)
|
||||
|
||||
return swarmMatches && driverMatches && stateMatches && nameMatches && labelMatches
|
||||
}
|
||||
|
||||
func matchesSwarmName(host *host.Host, swarmNames []string, swarmMasters map[string]string) bool {
|
||||
if len(swarmNames) == 0 {
|
||||
return true
|
||||
}
|
||||
for _, n := range swarmNames {
|
||||
if host.HostOptions != nil && host.HostOptions.SwarmOptions != nil {
|
||||
if strings.EqualFold(n, swarmMasters[host.HostOptions.SwarmOptions.Discovery]) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func matchesDriverName(host *host.Host, driverNames []string) bool {
|
||||
if len(driverNames) == 0 {
|
||||
return true
|
||||
}
|
||||
for _, n := range driverNames {
|
||||
if strings.EqualFold(host.DriverName, n) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func matchesState(host *host.Host, states []string) bool {
|
||||
if len(states) == 0 {
|
||||
return true
|
||||
}
|
||||
for _, n := range states {
|
||||
s, err := host.Driver.GetState()
|
||||
if err != nil {
|
||||
log.Warn(err)
|
||||
}
|
||||
if strings.EqualFold(n, s.String()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func matchesName(host *host.Host, names []string) bool {
|
||||
if len(names) == 0 {
|
||||
return true
|
||||
}
|
||||
for _, n := range names {
|
||||
r, err := regexp.Compile(n)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
os.Exit(1) // TODO: Can we get rid of this call, and exit 'properly' ?
|
||||
}
|
||||
if r.MatchString(host.Driver.GetMachineName()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func matchesLabel(host *host.Host, labels []string) bool {
|
||||
if len(labels) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
var englabels = make(map[string]string, len(host.HostOptions.EngineOptions.Labels))
|
||||
|
||||
if host.HostOptions != nil && host.HostOptions.EngineOptions.Labels != nil {
|
||||
for _, s := range host.HostOptions.EngineOptions.Labels {
|
||||
kv := strings.SplitN(s, "=", 2)
|
||||
englabels[kv[0]] = kv[1]
|
||||
}
|
||||
}
|
||||
|
||||
for _, l := range labels {
|
||||
kv := strings.SplitN(l, "=", 2)
|
||||
if val, exists := englabels[kv[0]]; exists && strings.EqualFold(val, kv[1]) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// PERFORMANCE: The code of this function is complicated because we try
|
||||
// to call the underlying drivers as less as possible to get the information
|
||||
// we need.
|
||||
func attemptGetHostState(h *host.Host, stateQueryChan chan<- HostListItem) {
|
||||
requestBeginning := time.Now()
|
||||
url := ""
|
||||
currentState := state.None
|
||||
dockerVersion := "Unknown"
|
||||
hostError := ""
|
||||
|
||||
url, err := h.URL()
|
||||
|
||||
// PERFORMANCE: if we have the url, it's ok to assume the host is running
|
||||
// This reduces the number of calls to the drivers
|
||||
if err == nil {
|
||||
if url != "" {
|
||||
currentState = state.Running
|
||||
} else {
|
||||
currentState, err = h.Driver.GetState()
|
||||
}
|
||||
} else {
|
||||
currentState, _ = h.Driver.GetState()
|
||||
}
|
||||
|
||||
if err == nil && url != "" {
|
||||
// PERFORMANCE: Reuse the url instead of asking the host again.
|
||||
// This reduces the number of calls to the drivers
|
||||
dockerHost := &mcndockerclient.RemoteDocker{
|
||||
HostURL: url,
|
||||
AuthOption: h.AuthOptions(),
|
||||
}
|
||||
dockerVersion, err = mcndockerclient.DockerVersion(dockerHost)
|
||||
|
||||
if err != nil {
|
||||
dockerVersion = "Unknown"
|
||||
} else {
|
||||
dockerVersion = fmt.Sprintf("v%s", dockerVersion)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
hostError = err.Error()
|
||||
}
|
||||
if hostError == drivers.ErrHostIsNotRunning.Error() {
|
||||
hostError = ""
|
||||
}
|
||||
|
||||
var swarmOptions *swarm.Options
|
||||
var engineOptions *engine.Options
|
||||
if h.HostOptions != nil {
|
||||
swarmOptions = h.HostOptions.SwarmOptions
|
||||
engineOptions = h.HostOptions.EngineOptions
|
||||
}
|
||||
|
||||
isMaster := false
|
||||
swarmHost := ""
|
||||
if swarmOptions != nil {
|
||||
isMaster = swarmOptions.Master
|
||||
swarmHost = swarmOptions.Host
|
||||
}
|
||||
|
||||
activeHost := isActive(currentState, url)
|
||||
activeSwarm := isSwarmActive(currentState, url, isMaster, swarmHost)
|
||||
active := "-"
|
||||
if activeHost {
|
||||
active = "*"
|
||||
}
|
||||
if activeSwarm {
|
||||
active = "* (swarm)"
|
||||
}
|
||||
|
||||
stateQueryChan <- HostListItem{
|
||||
Name: h.Name,
|
||||
Active: active,
|
||||
ActiveHost: activeHost,
|
||||
ActiveSwarm: activeSwarm,
|
||||
DriverName: h.Driver.DriverName(),
|
||||
State: currentState,
|
||||
URL: url,
|
||||
SwarmOptions: swarmOptions,
|
||||
EngineOptions: engineOptions,
|
||||
DockerVersion: dockerVersion,
|
||||
Error: hostError,
|
||||
ResponseTime: time.Now().Round(time.Millisecond).Sub(requestBeginning.Round(time.Millisecond)),
|
||||
}
|
||||
}
|
||||
|
||||
func getHostState(h *host.Host, hostListItemsChan chan<- HostListItem, timeout time.Duration) {
|
||||
// This channel is used to communicate the properties we are querying
|
||||
// about the host in the case of a successful read.
|
||||
stateQueryChan := make(chan HostListItem)
|
||||
|
||||
go attemptGetHostState(h, stateQueryChan)
|
||||
|
||||
select {
|
||||
// If we get back useful information, great. Forward it straight to
|
||||
// the original parent channel.
|
||||
case hli := <-stateQueryChan:
|
||||
hostListItemsChan <- hli
|
||||
|
||||
// Otherwise, give up after a predetermined duration.
|
||||
case <-time.After(timeout):
|
||||
hostListItemsChan <- HostListItem{
|
||||
Name: h.Name,
|
||||
DriverName: h.Driver.DriverName(),
|
||||
State: state.Timeout,
|
||||
ResponseTime: timeout,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getHostListItems(hostList []*host.Host, hostsInError map[string]error, timeout time.Duration) []HostListItem {
|
||||
log.Debugf("timeout set to %s", timeout)
|
||||
|
||||
hostListItems := []HostListItem{}
|
||||
hostListItemsChan := make(chan HostListItem)
|
||||
|
||||
for _, h := range hostList {
|
||||
go getHostState(h, hostListItemsChan, timeout)
|
||||
}
|
||||
|
||||
for range hostList {
|
||||
hostListItems = append(hostListItems, <-hostListItemsChan)
|
||||
}
|
||||
|
||||
close(hostListItemsChan)
|
||||
|
||||
for name, err := range hostsInError {
|
||||
hostListItems = append(hostListItems, newHostListItemInError(name, err))
|
||||
}
|
||||
|
||||
sortHostListItemsByName(hostListItems)
|
||||
return hostListItems
|
||||
}
|
||||
|
||||
func newHostListItemInError(name string, err error) HostListItem {
|
||||
return HostListItem{
|
||||
Name: name,
|
||||
DriverName: "not found",
|
||||
State: state.Error,
|
||||
Error: strings.Replace(err.Error(), "\n", " ", -1),
|
||||
}
|
||||
}
|
||||
|
||||
func sortHostListItemsByName(items []HostListItem) {
|
||||
m := make(map[string]HostListItem, len(items))
|
||||
s := make([]string, len(items))
|
||||
for i, v := range items {
|
||||
name := strings.ToLower(v.Name)
|
||||
m[name] = v
|
||||
s[i] = name
|
||||
}
|
||||
sort.Sort(naturalsort.NaturalSort(s))
|
||||
for i, v := range s {
|
||||
items[i] = m[v]
|
||||
}
|
||||
}
|
||||
|
||||
func isActive(currentState state.State, hostURL string) bool {
|
||||
return currentState == state.Running && hostURL == os.Getenv("DOCKER_HOST")
|
||||
}
|
||||
|
||||
func isSwarmActive(currentState state.State, hostURL string, isMaster bool, swarmHost string) bool {
|
||||
return isMaster && currentState == state.Running && toSwarmURL(hostURL, swarmHost) == os.Getenv("DOCKER_HOST")
|
||||
}
|
||||
|
||||
func urlPort(urlWithPort string) string {
|
||||
parts := strings.Split(urlWithPort, ":")
|
||||
return parts[len(parts)-1]
|
||||
}
|
||||
|
||||
func toSwarmURL(hostURL string, swarmHost string) string {
|
||||
hostPort := urlPort(hostURL)
|
||||
swarmPort := urlPort(swarmHost)
|
||||
return strings.Replace(hostURL, ":"+hostPort, ":"+swarmPort, 1)
|
||||
}
|
||||
|
|
@ -0,0 +1,535 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"time"
|
||||
|
||||
"errors"
|
||||
|
||||
"github.com/docker/machine/drivers/fakedriver"
|
||||
"github.com/docker/machine/libmachine/engine"
|
||||
"github.com/docker/machine/libmachine/host"
|
||||
"github.com/docker/machine/libmachine/mcndockerclient"
|
||||
"github.com/docker/machine/libmachine/state"
|
||||
"github.com/docker/machine/libmachine/swarm"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestParseFiltersErrorsGivenInvalidFilter(t *testing.T) {
|
||||
_, err := parseFilters([]string{"foo=bar"})
|
||||
assert.EqualError(t, err, "Unsupported filter key 'foo'")
|
||||
}
|
||||
|
||||
func TestParseFiltersSwarm(t *testing.T) {
|
||||
actual, _ := parseFilters([]string{"swarm=foo"})
|
||||
assert.Equal(t, actual, FilterOptions{SwarmName: []string{"foo"}})
|
||||
}
|
||||
|
||||
func TestParseFiltersDriver(t *testing.T) {
|
||||
actual, _ := parseFilters([]string{"driver=bar"})
|
||||
assert.Equal(t, actual, FilterOptions{DriverName: []string{"bar"}})
|
||||
}
|
||||
|
||||
func TestParseFiltersState(t *testing.T) {
|
||||
actual, _ := parseFilters([]string{"state=Running"})
|
||||
assert.Equal(t, actual, FilterOptions{State: []string{"Running"}})
|
||||
}
|
||||
|
||||
func TestParseFiltersName(t *testing.T) {
|
||||
actual, _ := parseFilters([]string{"name=dev"})
|
||||
assert.Equal(t, actual, FilterOptions{Name: []string{"dev"}})
|
||||
}
|
||||
|
||||
func TestParseFiltersLabel(t *testing.T) {
|
||||
actual, err := parseFilters([]string{"label=com.example.foo=bar"})
|
||||
assert.EqualValues(t, actual, FilterOptions{Labels: []string{"com.example.foo=bar"}})
|
||||
assert.Nil(t, err, "returned err value must be Nil")
|
||||
}
|
||||
|
||||
func TestParseFiltersAll(t *testing.T) {
|
||||
actual, _ := parseFilters([]string{"swarm=foo", "driver=bar", "state=Stopped", "name=dev"})
|
||||
assert.Equal(t, actual, FilterOptions{SwarmName: []string{"foo"}, DriverName: []string{"bar"}, State: []string{"Stopped"}, Name: []string{"dev"}})
|
||||
}
|
||||
|
||||
func TestParseFiltersAllCase(t *testing.T) {
|
||||
actual, err := parseFilters([]string{"sWarM=foo", "DrIver=bar", "StaTe=Stopped", "NAMe=dev", "LABEL=com=foo"})
|
||||
assert.Equal(t, actual, FilterOptions{SwarmName: []string{"foo"}, DriverName: []string{"bar"}, State: []string{"Stopped"}, Name: []string{"dev"}, Labels: []string{"com=foo"}})
|
||||
assert.Nil(t, err, "err should be nil")
|
||||
}
|
||||
|
||||
func TestParseFiltersDuplicates(t *testing.T) {
|
||||
actual, _ := parseFilters([]string{"swarm=foo", "driver=bar", "name=mark", "swarm=baz", "driver=qux", "state=Running", "state=Starting", "name=time"})
|
||||
assert.Equal(t, actual, FilterOptions{SwarmName: []string{"foo", "baz"}, DriverName: []string{"bar", "qux"}, State: []string{"Running", "Starting"}, Name: []string{"mark", "time"}})
|
||||
}
|
||||
|
||||
func TestParseFiltersValueWithEqual(t *testing.T) {
|
||||
actual, _ := parseFilters([]string{"driver=bar=baz"})
|
||||
assert.Equal(t, actual, FilterOptions{DriverName: []string{"bar=baz"}})
|
||||
}
|
||||
|
||||
func TestFilterHostsReturnsFiltersValuesCaseInsensitive(t *testing.T) {
|
||||
opts := FilterOptions{
|
||||
SwarmName: []string{"fOo"},
|
||||
DriverName: []string{"ViRtUaLboX"},
|
||||
State: []string{"StOPpeD"},
|
||||
Labels: []string{"com.EXAMPLE.app=FOO"},
|
||||
}
|
||||
hosts := []*host.Host{}
|
||||
actual := filterHosts(hosts, opts)
|
||||
assert.EqualValues(t, actual, hosts)
|
||||
}
|
||||
func TestFilterHostsReturnsSameGivenNoFilters(t *testing.T) {
|
||||
opts := FilterOptions{}
|
||||
hosts := []*host.Host{
|
||||
{
|
||||
Name: "testhost",
|
||||
DriverName: "fakedriver",
|
||||
},
|
||||
}
|
||||
actual := filterHosts(hosts, opts)
|
||||
assert.EqualValues(t, actual, hosts)
|
||||
}
|
||||
|
||||
func TestFilterHostsReturnSetLabel(t *testing.T) {
|
||||
opts := FilterOptions{
|
||||
Labels: []string{"com.class.foo=bar"},
|
||||
}
|
||||
hosts := []*host.Host{
|
||||
{
|
||||
Name: "testhost",
|
||||
DriverName: "fakedriver",
|
||||
HostOptions: &host.Options{
|
||||
EngineOptions: &engine.Options{
|
||||
Labels: []string{"com.class.foo=bar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
actual := filterHosts(hosts, opts)
|
||||
assert.EqualValues(t, actual, hosts)
|
||||
}
|
||||
|
||||
func TestFilterHostsReturnsEmptyGivenEmptyHosts(t *testing.T) {
|
||||
opts := FilterOptions{
|
||||
SwarmName: []string{"foo"},
|
||||
}
|
||||
hosts := []*host.Host{}
|
||||
assert.Empty(t, filterHosts(hosts, opts))
|
||||
}
|
||||
|
||||
func TestFilterHostsReturnsEmptyGivenNonMatchingFilters(t *testing.T) {
|
||||
opts := FilterOptions{
|
||||
SwarmName: []string{"foo"},
|
||||
}
|
||||
hosts := []*host.Host{
|
||||
{
|
||||
Name: "testhost",
|
||||
DriverName: "fakedriver",
|
||||
},
|
||||
}
|
||||
assert.Empty(t, filterHosts(hosts, opts))
|
||||
}
|
||||
|
||||
func TestFilterHostsBySwarmName(t *testing.T) {
|
||||
opts := FilterOptions{
|
||||
SwarmName: []string{"master"},
|
||||
}
|
||||
master :=
|
||||
&host.Host{
|
||||
Name: "master",
|
||||
HostOptions: &host.Options{
|
||||
SwarmOptions: &swarm.Options{Master: true, Discovery: "foo"},
|
||||
},
|
||||
}
|
||||
node1 :=
|
||||
&host.Host{
|
||||
Name: "node1",
|
||||
HostOptions: &host.Options{
|
||||
SwarmOptions: &swarm.Options{Master: false, Discovery: "foo"},
|
||||
},
|
||||
}
|
||||
othermaster :=
|
||||
&host.Host{
|
||||
Name: "othermaster",
|
||||
HostOptions: &host.Options{
|
||||
SwarmOptions: &swarm.Options{Master: true, Discovery: "bar"},
|
||||
},
|
||||
}
|
||||
hosts := []*host.Host{master, node1, othermaster}
|
||||
expected := []*host.Host{master, node1}
|
||||
|
||||
assert.EqualValues(t, filterHosts(hosts, opts), expected)
|
||||
}
|
||||
|
||||
func TestFilterHostsByDriverName(t *testing.T) {
|
||||
opts := FilterOptions{
|
||||
DriverName: []string{"fakedriver"},
|
||||
}
|
||||
node1 :=
|
||||
&host.Host{
|
||||
Name: "node1",
|
||||
DriverName: "fakedriver",
|
||||
}
|
||||
node2 :=
|
||||
&host.Host{
|
||||
Name: "node2",
|
||||
DriverName: "virtualbox",
|
||||
}
|
||||
node3 :=
|
||||
&host.Host{
|
||||
Name: "node3",
|
||||
DriverName: "fakedriver",
|
||||
}
|
||||
hosts := []*host.Host{node1, node2, node3}
|
||||
expected := []*host.Host{node1, node3}
|
||||
|
||||
assert.EqualValues(t, filterHosts(hosts, opts), expected)
|
||||
}
|
||||
|
||||
func TestFilterHostsByState(t *testing.T) {
|
||||
opts := FilterOptions{
|
||||
State: []string{"Paused", "Saved", "Stopped"},
|
||||
}
|
||||
node1 :=
|
||||
&host.Host{
|
||||
Name: "node1",
|
||||
DriverName: "fakedriver",
|
||||
Driver: &fakedriver.Driver{MockState: state.Paused},
|
||||
}
|
||||
node2 :=
|
||||
&host.Host{
|
||||
Name: "node2",
|
||||
DriverName: "virtualbox",
|
||||
Driver: &fakedriver.Driver{MockState: state.Stopped},
|
||||
}
|
||||
node3 :=
|
||||
&host.Host{
|
||||
Name: "node3",
|
||||
DriverName: "fakedriver",
|
||||
Driver: &fakedriver.Driver{MockState: state.Running},
|
||||
}
|
||||
hosts := []*host.Host{node1, node2, node3}
|
||||
expected := []*host.Host{node1, node2}
|
||||
|
||||
assert.EqualValues(t, filterHosts(hosts, opts), expected)
|
||||
}
|
||||
|
||||
func TestFilterHostsByName(t *testing.T) {
|
||||
opts := FilterOptions{
|
||||
Name: []string{"fire", "ice", "earth", "a.?r"},
|
||||
}
|
||||
node1 :=
|
||||
&host.Host{
|
||||
Name: "fire",
|
||||
DriverName: "fakedriver",
|
||||
Driver: &fakedriver.Driver{MockState: state.Paused, MockName: "fire"},
|
||||
}
|
||||
node2 :=
|
||||
&host.Host{
|
||||
Name: "ice",
|
||||
DriverName: "adriver",
|
||||
Driver: &fakedriver.Driver{MockState: state.Paused, MockName: "ice"},
|
||||
}
|
||||
node3 :=
|
||||
&host.Host{
|
||||
Name: "air",
|
||||
DriverName: "nodriver",
|
||||
Driver: &fakedriver.Driver{MockState: state.Paused, MockName: "air"},
|
||||
}
|
||||
node4 :=
|
||||
&host.Host{
|
||||
Name: "water",
|
||||
DriverName: "falsedriver",
|
||||
Driver: &fakedriver.Driver{MockState: state.Paused, MockName: "water"},
|
||||
}
|
||||
hosts := []*host.Host{node1, node2, node3, node4}
|
||||
expected := []*host.Host{node1, node2, node3}
|
||||
|
||||
assert.EqualValues(t, filterHosts(hosts, opts), expected)
|
||||
}
|
||||
|
||||
func TestFilterHostsMultiFlags(t *testing.T) {
|
||||
opts := FilterOptions{
|
||||
SwarmName: []string{},
|
||||
DriverName: []string{"fakedriver", "virtualbox"},
|
||||
}
|
||||
node1 :=
|
||||
&host.Host{
|
||||
Name: "node1",
|
||||
DriverName: "fakedriver",
|
||||
}
|
||||
node2 :=
|
||||
&host.Host{
|
||||
Name: "node2",
|
||||
DriverName: "virtualbox",
|
||||
}
|
||||
node3 :=
|
||||
&host.Host{
|
||||
Name: "node3",
|
||||
DriverName: "softlayer",
|
||||
}
|
||||
hosts := []*host.Host{node1, node2, node3}
|
||||
expected := []*host.Host{node1, node2}
|
||||
|
||||
assert.EqualValues(t, filterHosts(hosts, opts), expected)
|
||||
}
|
||||
|
||||
func TestFilterHostsDifferentFlagsProduceAND(t *testing.T) {
|
||||
opts := FilterOptions{
|
||||
DriverName: []string{"virtualbox"},
|
||||
State: []string{"Running"},
|
||||
}
|
||||
|
||||
hosts := []*host.Host{
|
||||
{
|
||||
Name: "node1",
|
||||
DriverName: "fakedriver",
|
||||
Driver: &fakedriver.Driver{MockState: state.Paused},
|
||||
},
|
||||
{
|
||||
Name: "node2",
|
||||
DriverName: "virtualbox",
|
||||
Driver: &fakedriver.Driver{MockState: state.Stopped},
|
||||
},
|
||||
{
|
||||
Name: "node3",
|
||||
DriverName: "fakedriver",
|
||||
Driver: &fakedriver.Driver{MockState: state.Running},
|
||||
},
|
||||
}
|
||||
|
||||
assert.Empty(t, filterHosts(hosts, opts))
|
||||
}
|
||||
|
||||
func TestGetHostListItems(t *testing.T) {
|
||||
defer func(versioner mcndockerclient.DockerVersioner) { mcndockerclient.CurrentDockerVersioner = versioner }(mcndockerclient.CurrentDockerVersioner)
|
||||
mcndockerclient.CurrentDockerVersioner = &mcndockerclient.FakeDockerVersioner{Version: "1.9"}
|
||||
|
||||
// TODO: Ideally this would mockable via interface instead.
|
||||
defer func(host string) { os.Setenv("DOCKER_HOST", host) }(os.Getenv("DOCKER_HOST"))
|
||||
os.Setenv("DOCKER_HOST", "tcp://active.host.com:2376")
|
||||
|
||||
hosts := []*host.Host{
|
||||
{
|
||||
Name: "foo",
|
||||
Driver: &fakedriver.Driver{
|
||||
MockState: state.Running,
|
||||
MockIP: "active.host.com",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "bar100",
|
||||
Driver: &fakedriver.Driver{
|
||||
MockState: state.Stopped,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "bar10",
|
||||
Driver: &fakedriver.Driver{
|
||||
MockState: state.Error,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
expected := []struct {
|
||||
name string
|
||||
state state.State
|
||||
active bool
|
||||
version string
|
||||
error string
|
||||
}{
|
||||
{"bar10", state.Error, false, "Unknown", "Unable to get ip"},
|
||||
{"bar100", state.Stopped, false, "Unknown", ""},
|
||||
{"foo", state.Running, true, "v1.9", ""},
|
||||
}
|
||||
|
||||
items := getHostListItems(hosts, map[string]error{}, 10*time.Second)
|
||||
|
||||
for i := range expected {
|
||||
assert.Equal(t, expected[i].name, items[i].Name)
|
||||
assert.Equal(t, expected[i].state, items[i].State)
|
||||
assert.Equal(t, expected[i].active, items[i].ActiveHost)
|
||||
assert.Equal(t, expected[i].version, items[i].DockerVersion)
|
||||
assert.Equal(t, expected[i].error, items[i].Error)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetHostListItemsEnvDockerHostUnset(t *testing.T) {
|
||||
defer func(versioner mcndockerclient.DockerVersioner) { mcndockerclient.CurrentDockerVersioner = versioner }(mcndockerclient.CurrentDockerVersioner)
|
||||
mcndockerclient.CurrentDockerVersioner = &mcndockerclient.FakeDockerVersioner{Version: "1.9"}
|
||||
|
||||
defer func(host string) { os.Setenv("DOCKER_HOST", host) }(os.Getenv("DOCKER_HOST"))
|
||||
os.Unsetenv("DOCKER_HOST")
|
||||
|
||||
hosts := []*host.Host{
|
||||
{
|
||||
Name: "foo",
|
||||
Driver: &fakedriver.Driver{
|
||||
MockState: state.Running,
|
||||
MockIP: "120.0.0.1",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "bar",
|
||||
Driver: &fakedriver.Driver{
|
||||
MockState: state.Stopped,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "baz",
|
||||
Driver: &fakedriver.Driver{
|
||||
MockState: state.Saved,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
expected := map[string]struct {
|
||||
state state.State
|
||||
active bool
|
||||
}{
|
||||
"foo": {state.Running, false},
|
||||
"bar": {state.Stopped, false},
|
||||
"baz": {state.Saved, false},
|
||||
}
|
||||
|
||||
items := getHostListItems(hosts, map[string]error{}, 10*time.Second)
|
||||
|
||||
for _, item := range items {
|
||||
expected := expected[item.Name]
|
||||
|
||||
assert.Equal(t, expected.state, item.State)
|
||||
assert.Equal(t, expected.active, item.ActiveHost)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsActive(t *testing.T) {
|
||||
cases := []struct {
|
||||
dockerHost string
|
||||
state state.State
|
||||
expected bool
|
||||
}{
|
||||
{"", state.Running, false},
|
||||
{"tcp://5.6.7.8:2376", state.Running, false},
|
||||
{"tcp://1.2.3.4:2376", state.Stopped, false},
|
||||
{"tcp://1.2.3.4:2376", state.Running, true},
|
||||
{"tcp://1.2.3.4:3376", state.Running, false},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
os.Unsetenv("DOCKER_HOST")
|
||||
if c.dockerHost != "" {
|
||||
os.Setenv("DOCKER_HOST", c.dockerHost)
|
||||
}
|
||||
|
||||
actual := isActive(c.state, "tcp://1.2.3.4:2376")
|
||||
|
||||
assert.Equal(t, c.expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsSwarmActive(t *testing.T) {
|
||||
cases := []struct {
|
||||
dockerHost string
|
||||
state state.State
|
||||
isMaster bool
|
||||
expected bool
|
||||
}{
|
||||
{"", state.Running, false, false},
|
||||
{"tcp://5.6.7.8:3376", state.Running, true, false},
|
||||
{"tcp://1.2.3.4:3376", state.Stopped, true, false},
|
||||
{"tcp://1.2.3.4:3376", state.Running, true, true},
|
||||
{"tcp://1.2.3.4:3376", state.Running, false, false},
|
||||
{"tcp://1.2.3.4:2376", state.Running, true, false},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
os.Unsetenv("DOCKER_HOST")
|
||||
if c.dockerHost != "" {
|
||||
os.Setenv("DOCKER_HOST", c.dockerHost)
|
||||
}
|
||||
|
||||
actual := isSwarmActive(c.state, "tcp://1.2.3.4:2376", c.isMaster, "tcp://0.0.0.0:3376")
|
||||
|
||||
assert.Equal(t, c.expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetHostStateTimeout(t *testing.T) {
|
||||
hosts := []*host.Host{
|
||||
{
|
||||
Name: "foo",
|
||||
Driver: &fakedriver.Driver{
|
||||
MockState: state.Timeout,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
hostItem := getHostListItems(hosts, nil, time.Millisecond)[0]
|
||||
|
||||
assert.Equal(t, "foo", hostItem.Name)
|
||||
assert.Equal(t, state.Timeout, hostItem.State)
|
||||
assert.Equal(t, "Driver", hostItem.DriverName)
|
||||
assert.Equal(t, time.Millisecond, hostItem.ResponseTime)
|
||||
}
|
||||
|
||||
func TestGetHostStateError(t *testing.T) {
|
||||
hosts := []*host.Host{
|
||||
{
|
||||
Name: "foo",
|
||||
Driver: &fakedriver.Driver{
|
||||
MockState: state.Error,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
hostItem := getHostListItems(hosts, nil, 10*time.Second)[0]
|
||||
|
||||
assert.Equal(t, "foo", hostItem.Name)
|
||||
assert.Equal(t, state.Error, hostItem.State)
|
||||
assert.Equal(t, "Driver", hostItem.DriverName)
|
||||
assert.Empty(t, hostItem.URL)
|
||||
assert.Equal(t, "Unable to get ip", hostItem.Error)
|
||||
assert.Nil(t, hostItem.SwarmOptions)
|
||||
}
|
||||
|
||||
func TestGetSomeHostInError(t *testing.T) {
|
||||
defer func(versioner mcndockerclient.DockerVersioner) { mcndockerclient.CurrentDockerVersioner = versioner }(mcndockerclient.CurrentDockerVersioner)
|
||||
mcndockerclient.CurrentDockerVersioner = &mcndockerclient.FakeDockerVersioner{Version: "1.9"}
|
||||
|
||||
hosts := []*host.Host{
|
||||
{
|
||||
Name: "foo",
|
||||
Driver: &fakedriver.Driver{
|
||||
MockState: state.Running,
|
||||
},
|
||||
},
|
||||
}
|
||||
hostsInError := map[string]error{
|
||||
"bar": errors.New("invalid memory address or nil pointer dereference"),
|
||||
}
|
||||
|
||||
hostItems := getHostListItems(hosts, hostsInError, 10*time.Second)
|
||||
assert.Equal(t, 2, len(hostItems))
|
||||
|
||||
hostItem := hostItems[0]
|
||||
assert.Equal(t, "bar", hostItem.Name)
|
||||
assert.Equal(t, state.Error, hostItem.State)
|
||||
assert.Equal(t, "not found", hostItem.DriverName)
|
||||
assert.Empty(t, hostItem.URL)
|
||||
assert.Equal(t, "invalid memory address or nil pointer dereference", hostItem.Error)
|
||||
assert.Nil(t, hostItem.SwarmOptions)
|
||||
|
||||
hostItem = hostItems[1]
|
||||
assert.Equal(t, "foo", hostItem.Name)
|
||||
assert.Equal(t, state.Running, hostItem.State)
|
||||
}
|
||||
|
||||
func TestOnErrorWithMultilineComment(t *testing.T) {
|
||||
err := errors.New("MissingParameter: The request must contain the parameter InstanceId\n status code: 400, request id:")
|
||||
|
||||
itemInError := newHostListItemInError("foo", err)
|
||||
|
||||
assert.Equal(t, itemInError.Error, "MissingParameter: The request must contain the parameter InstanceId status code: 400, request id:")
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package mcndirs
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/docker/machine/libmachine/mcnutils"
|
||||
)
|
||||
|
||||
var (
|
||||
BaseDir = os.Getenv("MACHINE_STORAGE_PATH")
|
||||
)
|
||||
|
||||
func GetBaseDir() string {
|
||||
if BaseDir == "" {
|
||||
BaseDir = filepath.Join(mcnutils.GetHomeDir(), ".docker", "machine")
|
||||
}
|
||||
return BaseDir
|
||||
}
|
||||
|
||||
func GetMachineDir() string {
|
||||
return filepath.Join(GetBaseDir(), "machines")
|
||||
}
|
||||
|
||||
func GetMachineCertDir() string {
|
||||
return filepath.Join(GetBaseDir(), "certs")
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
package mcndirs
|
||||
|
||||
import (
|
||||
"path"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/machine/libmachine/mcnutils"
|
||||
)
|
||||
|
||||
func TestGetBaseDir(t *testing.T) {
|
||||
// reset any override env var
|
||||
BaseDir = ""
|
||||
|
||||
homeDir := mcnutils.GetHomeDir()
|
||||
baseDir := GetBaseDir()
|
||||
|
||||
if strings.Index(baseDir, homeDir) != 0 {
|
||||
t.Fatalf("expected base dir with prefix %s; received %s", homeDir, baseDir)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetCustomBaseDir(t *testing.T) {
|
||||
root := "/tmp"
|
||||
BaseDir = root
|
||||
baseDir := GetBaseDir()
|
||||
|
||||
if strings.Index(baseDir, root) != 0 {
|
||||
t.Fatalf("expected base dir with prefix %s; received %s", root, baseDir)
|
||||
}
|
||||
BaseDir = ""
|
||||
}
|
||||
|
||||
func TestGetMachineDir(t *testing.T) {
|
||||
root := "/tmp"
|
||||
BaseDir = root
|
||||
machineDir := GetMachineDir()
|
||||
|
||||
if strings.Index(machineDir, root) != 0 {
|
||||
t.Fatalf("expected machine dir with prefix %s; received %s", root, machineDir)
|
||||
}
|
||||
|
||||
path, filename := path.Split(machineDir)
|
||||
if strings.Index(path, root) != 0 {
|
||||
t.Fatalf("expected base path of %s; received %s", root, path)
|
||||
}
|
||||
if filename != "machines" {
|
||||
t.Fatalf("expected machine dir \"machines\"; received %s", filename)
|
||||
}
|
||||
BaseDir = ""
|
||||
}
|
||||
|
||||
func TestGetMachineCertDir(t *testing.T) {
|
||||
root := "/tmp"
|
||||
BaseDir = root
|
||||
clientDir := GetMachineCertDir()
|
||||
|
||||
if strings.Index(clientDir, root) != 0 {
|
||||
t.Fatalf("expected machine client cert dir with prefix %s; received %s", root, clientDir)
|
||||
}
|
||||
|
||||
path, filename := path.Split(clientDir)
|
||||
if strings.Index(path, root) != 0 {
|
||||
t.Fatalf("expected base path of %s; received %s", root, path)
|
||||
}
|
||||
if filename != "certs" {
|
||||
t.Fatalf("expected machine client dir \"certs\"; received %s", filename)
|
||||
}
|
||||
BaseDir = ""
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package commands
|
||||
|
||||
import "github.com/docker/machine/libmachine"
|
||||
|
||||
func cmdProvision(c CommandLine, api libmachine.API) error {
|
||||
return runAction("provision", c, api)
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/docker/machine/commands/commandstest"
|
||||
"github.com/docker/machine/drivers/fakedriver"
|
||||
"github.com/docker/machine/libmachine"
|
||||
"github.com/docker/machine/libmachine/auth"
|
||||
"github.com/docker/machine/libmachine/engine"
|
||||
"github.com/docker/machine/libmachine/host"
|
||||
"github.com/docker/machine/libmachine/libmachinetest"
|
||||
"github.com/docker/machine/libmachine/provision"
|
||||
"github.com/docker/machine/libmachine/swarm"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCmdProvision(t *testing.T) {
|
||||
testCases := []struct {
|
||||
commandLine CommandLine
|
||||
api libmachine.API
|
||||
expectedErr error
|
||||
}{
|
||||
{
|
||||
commandLine: &commandstest.FakeCommandLine{
|
||||
CliArgs: []string{"foo", "bar"},
|
||||
},
|
||||
api: &libmachinetest.FakeAPI{
|
||||
Hosts: []*host.Host{
|
||||
{
|
||||
Name: "foo",
|
||||
Driver: &fakedriver.Driver{},
|
||||
HostOptions: &host.Options{
|
||||
EngineOptions: &engine.Options{},
|
||||
AuthOptions: &auth.Options{},
|
||||
SwarmOptions: &swarm.Options{},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "bar",
|
||||
Driver: &fakedriver.Driver{},
|
||||
HostOptions: &host.Options{
|
||||
EngineOptions: &engine.Options{},
|
||||
AuthOptions: &auth.Options{},
|
||||
SwarmOptions: &swarm.Options{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
}
|
||||
|
||||
provision.SetDetector(&provision.FakeDetector{
|
||||
Provisioner: provision.NewFakeProvisioner(nil),
|
||||
})
|
||||
|
||||
// fakeprovisioner always returns "true" for compatible host, so we
|
||||
// just need to register it.
|
||||
provision.Register("fakeprovisioner", &provision.RegisteredProvisioner{
|
||||
New: provision.NewFakeProvisioner,
|
||||
})
|
||||
|
||||
for _, tc := range testCases {
|
||||
assert.Equal(t, tc.expectedErr, cmdProvision(tc.commandLine, tc.api))
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"github.com/docker/machine/libmachine"
|
||||
"github.com/docker/machine/libmachine/log"
|
||||
)
|
||||
|
||||
func cmdRegenerateCerts(c CommandLine, api libmachine.API) error {
|
||||
if !c.Bool("force") {
|
||||
ok, err := confirmInput("Regenerate TLS machine certs? Warning: this is irreversible.")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
log.Infof("Regenerating TLS certificates")
|
||||
|
||||
return runAction("configureAuth", c, api)
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"github.com/docker/machine/libmachine"
|
||||
"github.com/docker/machine/libmachine/log"
|
||||
)
|
||||
|
||||
func cmdRestart(c CommandLine, api libmachine.API) error {
|
||||
if err := runAction("restart", c, api); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Info("Restarted machines may have new IP addresses. You may need to re-run the `docker-machine env` command.")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"strings"
|
||||
|
||||
"errors"
|
||||
|
||||
"github.com/docker/machine/libmachine"
|
||||
"github.com/docker/machine/libmachine/log"
|
||||
)
|
||||
|
||||
func cmdRm(c CommandLine, api libmachine.API) error {
|
||||
if len(c.Args()) == 0 {
|
||||
c.ShowHelp()
|
||||
return ErrNoMachineSpecified
|
||||
}
|
||||
|
||||
log.Info(fmt.Sprintf("About to remove %s", strings.Join(c.Args(), ", ")))
|
||||
|
||||
force := c.Bool("force")
|
||||
confirm := c.Bool("y")
|
||||
var errorOccured []string
|
||||
|
||||
if !userConfirm(confirm, force) {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, hostName := range c.Args() {
|
||||
err := removeRemoteMachine(hostName, api)
|
||||
if err != nil {
|
||||
errorOccured = collectError(fmt.Sprintf("Error removing host %q: %s", hostName, err), force, errorOccured)
|
||||
}
|
||||
|
||||
if err == nil || force {
|
||||
removeErr := removeLocalMachine(hostName, api)
|
||||
if removeErr != nil {
|
||||
errorOccured = collectError(fmt.Sprintf("Can't remove \"%s\"", hostName), force, errorOccured)
|
||||
} else {
|
||||
log.Infof("Successfully removed %s", hostName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(errorOccured) > 0 && !force {
|
||||
return errors.New(strings.Join(errorOccured, "\n"))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func userConfirm(confirm bool, force bool) bool {
|
||||
if confirm || force {
|
||||
return true
|
||||
}
|
||||
|
||||
sure, err := confirmInput(fmt.Sprintf("Are you sure?"))
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return sure
|
||||
}
|
||||
|
||||
func removeRemoteMachine(hostName string, api libmachine.API) error {
|
||||
currentHost, loaderr := api.Load(hostName)
|
||||
if loaderr != nil {
|
||||
return loaderr
|
||||
}
|
||||
|
||||
return currentHost.Driver.Remove()
|
||||
}
|
||||
|
||||
func removeLocalMachine(hostName string, api libmachine.API) error {
|
||||
exist, _ := api.Exists(hostName)
|
||||
if !exist {
|
||||
return errors.New(hostName + " does not exist.")
|
||||
}
|
||||
return api.Remove(hostName)
|
||||
}
|
||||
|
||||
func collectError(message string, force bool, errorOccured []string) []string {
|
||||
if force {
|
||||
log.Error(message)
|
||||
}
|
||||
return append(errorOccured, message)
|
||||
}
|
||||
|
|
@ -0,0 +1,233 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"errors"
|
||||
|
||||
"github.com/docker/machine/commands/commandstest"
|
||||
"github.com/docker/machine/drivers/fakedriver"
|
||||
"github.com/docker/machine/libmachine/host"
|
||||
"github.com/docker/machine/libmachine/libmachinetest"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCmdRmMissingMachineName(t *testing.T) {
|
||||
commandLine := &commandstest.FakeCommandLine{}
|
||||
api := &libmachinetest.FakeAPI{}
|
||||
|
||||
err := cmdRm(commandLine, api)
|
||||
|
||||
assert.Equal(t, ErrNoMachineSpecified, err)
|
||||
assert.True(t, commandLine.HelpShown)
|
||||
}
|
||||
|
||||
func TestCmdRm(t *testing.T) {
|
||||
commandLine := &commandstest.FakeCommandLine{
|
||||
CliArgs: []string{"machineToRemove1", "machineToRemove2"},
|
||||
LocalFlags: &commandstest.FakeFlagger{
|
||||
Data: map[string]interface{}{
|
||||
"y": true,
|
||||
},
|
||||
},
|
||||
}
|
||||
api := &libmachinetest.FakeAPI{
|
||||
Hosts: []*host.Host{
|
||||
{
|
||||
Name: "machineToRemove1",
|
||||
Driver: &fakedriver.Driver{},
|
||||
},
|
||||
{
|
||||
Name: "machineToRemove2",
|
||||
Driver: &fakedriver.Driver{},
|
||||
},
|
||||
{
|
||||
Name: "machine",
|
||||
Driver: &fakedriver.Driver{},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := cmdRm(commandLine, api)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.False(t, libmachinetest.Exists(api, "machineToRemove1"))
|
||||
assert.False(t, libmachinetest.Exists(api, "machineToRemove2"))
|
||||
assert.True(t, libmachinetest.Exists(api, "machine"))
|
||||
}
|
||||
|
||||
func TestCmdRmforcefully(t *testing.T) {
|
||||
commandLine := &commandstest.FakeCommandLine{
|
||||
CliArgs: []string{"machineToRemove1", "machineToRemove2"},
|
||||
LocalFlags: &commandstest.FakeFlagger{
|
||||
Data: map[string]interface{}{
|
||||
"force": true,
|
||||
},
|
||||
},
|
||||
}
|
||||
api := &libmachinetest.FakeAPI{
|
||||
Hosts: []*host.Host{
|
||||
{
|
||||
Name: "machineToRemove1",
|
||||
Driver: &fakedriver.Driver{},
|
||||
},
|
||||
{
|
||||
Name: "machineToRemove2",
|
||||
Driver: &fakedriver.Driver{},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := cmdRm(commandLine, api)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.False(t, libmachinetest.Exists(api, "machineToRemove1"))
|
||||
assert.False(t, libmachinetest.Exists(api, "machineToRemove2"))
|
||||
}
|
||||
|
||||
func TestCmdRmforceDoesAutoConfirm(t *testing.T) {
|
||||
commandLine := &commandstest.FakeCommandLine{
|
||||
CliArgs: []string{"machineToRemove1", "machineToRemove2"},
|
||||
LocalFlags: &commandstest.FakeFlagger{
|
||||
Data: map[string]interface{}{
|
||||
"y": false,
|
||||
"force": true,
|
||||
},
|
||||
},
|
||||
}
|
||||
api := &libmachinetest.FakeAPI{
|
||||
Hosts: []*host.Host{
|
||||
{
|
||||
Name: "machineToRemove1",
|
||||
Driver: &fakedriver.Driver{},
|
||||
},
|
||||
{
|
||||
Name: "machineToRemove2",
|
||||
Driver: &fakedriver.Driver{},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := cmdRm(commandLine, api)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.False(t, libmachinetest.Exists(api, "machineToRemove1"))
|
||||
assert.False(t, libmachinetest.Exists(api, "machineToRemove2"))
|
||||
}
|
||||
|
||||
func TestCmdRmforceConfirmUnset(t *testing.T) {
|
||||
commandLine := &commandstest.FakeCommandLine{
|
||||
CliArgs: []string{"machineToRemove1"},
|
||||
LocalFlags: &commandstest.FakeFlagger{
|
||||
Data: map[string]interface{}{
|
||||
"y": false,
|
||||
"force": false,
|
||||
},
|
||||
},
|
||||
}
|
||||
api := &libmachinetest.FakeAPI{
|
||||
Hosts: []*host.Host{
|
||||
{
|
||||
Name: "machineToRemove1",
|
||||
Driver: &fakedriver.Driver{},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := cmdRm(commandLine, api)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.True(t, libmachinetest.Exists(api, "machineToRemove1"))
|
||||
}
|
||||
|
||||
type DriverWithRemoveWhichFail struct {
|
||||
fakedriver.Driver
|
||||
}
|
||||
|
||||
func (d *DriverWithRemoveWhichFail) Remove() error {
|
||||
return errors.New("unknown error")
|
||||
}
|
||||
|
||||
func TestDontStopWhenADriverRemovalFails(t *testing.T) {
|
||||
commandLine := &commandstest.FakeCommandLine{
|
||||
CliArgs: []string{"machineToRemove1", "machineToRemove2", "machineToRemove3"},
|
||||
LocalFlags: &commandstest.FakeFlagger{
|
||||
Data: map[string]interface{}{
|
||||
"y": true,
|
||||
},
|
||||
},
|
||||
}
|
||||
api := &libmachinetest.FakeAPI{
|
||||
Hosts: []*host.Host{
|
||||
{
|
||||
Name: "machineToRemove1",
|
||||
Driver: &fakedriver.Driver{},
|
||||
},
|
||||
{
|
||||
Name: "machineToRemove2",
|
||||
Driver: &DriverWithRemoveWhichFail{},
|
||||
},
|
||||
{
|
||||
Name: "machineToRemove3",
|
||||
Driver: &fakedriver.Driver{},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := cmdRm(commandLine, api)
|
||||
assert.EqualError(t, err, "Error removing host \"machineToRemove2\": unknown error")
|
||||
|
||||
assert.False(t, libmachinetest.Exists(api, "machineToRemove1"))
|
||||
assert.True(t, libmachinetest.Exists(api, "machineToRemove2"))
|
||||
assert.False(t, libmachinetest.Exists(api, "machineToRemove3"))
|
||||
}
|
||||
|
||||
func TestForceRemoveEvenWhenItFails(t *testing.T) {
|
||||
commandLine := &commandstest.FakeCommandLine{
|
||||
CliArgs: []string{"machineToRemove1"},
|
||||
LocalFlags: &commandstest.FakeFlagger{
|
||||
Data: map[string]interface{}{
|
||||
"y": true,
|
||||
"force": true,
|
||||
},
|
||||
},
|
||||
}
|
||||
api := &libmachinetest.FakeAPI{
|
||||
Hosts: []*host.Host{
|
||||
{
|
||||
Name: "machineToRemove1",
|
||||
Driver: &DriverWithRemoveWhichFail{},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := cmdRm(commandLine, api)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.False(t, libmachinetest.Exists(api, "machineToRemove1"))
|
||||
}
|
||||
|
||||
func TestDontRemoveMachineIsRemovalFailsAndNotForced(t *testing.T) {
|
||||
commandLine := &commandstest.FakeCommandLine{
|
||||
CliArgs: []string{"machineToRemove1"},
|
||||
LocalFlags: &commandstest.FakeFlagger{
|
||||
Data: map[string]interface{}{
|
||||
"y": true,
|
||||
"force": false,
|
||||
},
|
||||
},
|
||||
}
|
||||
api := &libmachinetest.FakeAPI{
|
||||
Hosts: []*host.Host{
|
||||
{
|
||||
Name: "machineToRemove1",
|
||||
Driver: &DriverWithRemoveWhichFail{},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := cmdRm(commandLine, api)
|
||||
assert.EqualError(t, err, "Error removing host \"machineToRemove1\": unknown error")
|
||||
|
||||
assert.True(t, libmachinetest.Exists(api, "machineToRemove1"))
|
||||
}
|
||||
|
|
@ -0,0 +1,170 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/machine/libmachine"
|
||||
"github.com/docker/machine/libmachine/log"
|
||||
"github.com/docker/machine/libmachine/persist"
|
||||
)
|
||||
|
||||
var (
|
||||
errWrongNumberArguments = errors.New("Improper number of arguments")
|
||||
|
||||
// TODO: possibly move this to ssh package
|
||||
baseSSHArgs = []string{
|
||||
"-o", "IdentitiesOnly=yes",
|
||||
"-o", "StrictHostKeyChecking=no",
|
||||
"-o", "UserKnownHostsFile=/dev/null",
|
||||
"-o", "LogLevel=quiet", // suppress "Warning: Permanently added '[localhost]:2022' (ECDSA) to the list of known hosts."
|
||||
}
|
||||
)
|
||||
|
||||
// HostInfo gives the mandatory information to connect to a host.
|
||||
type HostInfo interface {
|
||||
GetMachineName() string
|
||||
|
||||
GetIP() (string, error)
|
||||
|
||||
GetSSHUsername() string
|
||||
|
||||
GetSSHKeyPath() string
|
||||
}
|
||||
|
||||
// HostInfoLoader loads host information.
|
||||
type HostInfoLoader interface {
|
||||
load(name string) (HostInfo, error)
|
||||
}
|
||||
|
||||
type storeHostInfoLoader struct {
|
||||
store persist.Store
|
||||
}
|
||||
|
||||
func (s *storeHostInfoLoader) load(name string) (HostInfo, error) {
|
||||
host, err := s.store.Load(name)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error loading host: %s", err)
|
||||
}
|
||||
|
||||
return host.Driver, nil
|
||||
}
|
||||
|
||||
func cmdScp(c CommandLine, api libmachine.API) error {
|
||||
args := c.Args()
|
||||
if len(args) != 2 {
|
||||
c.ShowHelp()
|
||||
return errWrongNumberArguments
|
||||
}
|
||||
|
||||
src := args[0]
|
||||
dest := args[1]
|
||||
|
||||
hostInfoLoader := &storeHostInfoLoader{api}
|
||||
|
||||
cmd, err := getScpCmd(src, dest, c.Bool("recursive"), hostInfoLoader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return runCmdWithStdIo(*cmd)
|
||||
}
|
||||
|
||||
func getScpCmd(src, dest string, recursive bool, hostInfoLoader HostInfoLoader) (*exec.Cmd, error) {
|
||||
cmdPath, err := exec.LookPath("scp")
|
||||
if err != nil {
|
||||
return nil, errors.New("Error: You must have a copy of the scp binary locally to use the scp feature.")
|
||||
}
|
||||
|
||||
srcHost, srcPath, srcOpts, err := getInfoForScpArg(src, hostInfoLoader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
destHost, destPath, destOpts, err := getInfoForScpArg(dest, hostInfoLoader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO: Check that "-3" flag is available in user's version of scp.
|
||||
// It is on every system I've checked, but the manual mentioned it's "newer"
|
||||
sshArgs := baseSSHArgs
|
||||
sshArgs = append(sshArgs, "-3")
|
||||
if recursive {
|
||||
sshArgs = append(sshArgs, "-r")
|
||||
}
|
||||
|
||||
// Append needed -i / private key flags to command.
|
||||
sshArgs = append(sshArgs, srcOpts...)
|
||||
sshArgs = append(sshArgs, destOpts...)
|
||||
|
||||
// Append actual arguments for the scp command (i.e. docker@<ip>:/path)
|
||||
locationArg, err := generateLocationArg(srcHost, srcPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sshArgs = append(sshArgs, locationArg)
|
||||
locationArg, err = generateLocationArg(destHost, destPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sshArgs = append(sshArgs, locationArg)
|
||||
|
||||
cmd := exec.Command(cmdPath, sshArgs...)
|
||||
log.Debug(*cmd)
|
||||
return cmd, nil
|
||||
}
|
||||
|
||||
func getInfoForScpArg(hostAndPath string, hostInfoLoader HostInfoLoader) (HostInfo, string, []string, error) {
|
||||
// Local path. e.g. "/tmp/foo"
|
||||
if !strings.Contains(hostAndPath, ":") {
|
||||
return nil, hostAndPath, nil, nil
|
||||
}
|
||||
|
||||
// Path with hostname. e.g. "hostname:/usr/bin/cmatrix"
|
||||
parts := strings.SplitN(hostAndPath, ":", 2)
|
||||
hostName := parts[0]
|
||||
path := parts[1]
|
||||
if hostName == "localhost" {
|
||||
return nil, path, nil, nil
|
||||
}
|
||||
|
||||
// Remote path
|
||||
hostInfo, err := hostInfoLoader.load(hostName)
|
||||
if err != nil {
|
||||
return nil, "", nil, fmt.Errorf("Error loading host: %s", err)
|
||||
}
|
||||
|
||||
args := []string{}
|
||||
if hostInfo.GetSSHKeyPath() != "" {
|
||||
args = append(args, "-i", hostInfo.GetSSHKeyPath())
|
||||
}
|
||||
|
||||
return hostInfo, path, args, nil
|
||||
}
|
||||
|
||||
func generateLocationArg(hostInfo HostInfo, path string) (string, error) {
|
||||
if hostInfo == nil {
|
||||
return path, nil
|
||||
}
|
||||
|
||||
ip, err := hostInfo.GetIP()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
location := fmt.Sprintf("%s@%s:%s", hostInfo.GetSSHUsername(), ip, path)
|
||||
return location, nil
|
||||
}
|
||||
|
||||
func runCmdWithStdIo(cmd exec.Cmd) error {
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
return cmd.Run()
|
||||
}
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type MockHostInfo struct {
|
||||
name string
|
||||
ip string
|
||||
sshUsername string
|
||||
sshKeyPath string
|
||||
}
|
||||
|
||||
func (h *MockHostInfo) GetMachineName() string {
|
||||
return h.name
|
||||
}
|
||||
|
||||
func (h *MockHostInfo) GetIP() (string, error) {
|
||||
return h.ip, nil
|
||||
}
|
||||
|
||||
func (h *MockHostInfo) GetSSHUsername() string {
|
||||
return h.sshUsername
|
||||
}
|
||||
|
||||
func (h *MockHostInfo) GetSSHKeyPath() string {
|
||||
return h.sshKeyPath
|
||||
}
|
||||
|
||||
type MockHostInfoLoader struct {
|
||||
hostInfo MockHostInfo
|
||||
}
|
||||
|
||||
func (l *MockHostInfoLoader) load(name string) (HostInfo, error) {
|
||||
info := l.hostInfo
|
||||
info.name = name
|
||||
return &info, nil
|
||||
}
|
||||
|
||||
func TestGetInfoForLocalScpArg(t *testing.T) {
|
||||
host, path, opts, err := getInfoForScpArg("/tmp/foo", nil)
|
||||
assert.Nil(t, host)
|
||||
assert.Equal(t, "/tmp/foo", path)
|
||||
assert.Nil(t, opts)
|
||||
assert.NoError(t, err)
|
||||
|
||||
host, path, opts, err = getInfoForScpArg("localhost:C:\\path", nil)
|
||||
assert.Nil(t, host)
|
||||
assert.Equal(t, "C:\\path", path)
|
||||
assert.Nil(t, opts)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestGetInfoForRemoteScpArg(t *testing.T) {
|
||||
hostInfoLoader := MockHostInfoLoader{MockHostInfo{
|
||||
sshKeyPath: "/fake/keypath/id_rsa",
|
||||
}}
|
||||
|
||||
host, path, opts, err := getInfoForScpArg("myfunhost:/home/docker/foo", &hostInfoLoader)
|
||||
assert.Equal(t, "myfunhost", host.GetMachineName())
|
||||
assert.Equal(t, "/home/docker/foo", path)
|
||||
assert.Equal(t, []string{"-i", "/fake/keypath/id_rsa"}, opts)
|
||||
assert.NoError(t, err)
|
||||
|
||||
host, path, opts, err = getInfoForScpArg("myfunhost:C:\\path", &hostInfoLoader)
|
||||
assert.Equal(t, "myfunhost", host.GetMachineName())
|
||||
assert.Equal(t, "C:\\path", path)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestHostLocation(t *testing.T) {
|
||||
arg, err := generateLocationArg(nil, "/home/docker/foo")
|
||||
|
||||
assert.Equal(t, "/home/docker/foo", arg)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestRemoteLocation(t *testing.T) {
|
||||
hostInfo := MockHostInfo{
|
||||
ip: "12.34.56.78",
|
||||
sshUsername: "root",
|
||||
}
|
||||
|
||||
arg, err := generateLocationArg(&hostInfo, "/home/docker/foo")
|
||||
|
||||
assert.Equal(t, "root@12.34.56.78:/home/docker/foo", arg)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestGetScpCmd(t *testing.T) {
|
||||
hostInfoLoader := MockHostInfoLoader{MockHostInfo{
|
||||
ip: "12.34.56.78",
|
||||
sshUsername: "root",
|
||||
sshKeyPath: "/fake/keypath/id_rsa",
|
||||
}}
|
||||
|
||||
cmd, err := getScpCmd("/tmp/foo", "myfunhost:/home/docker/foo", true, &hostInfoLoader)
|
||||
|
||||
expectedArgs := append(
|
||||
baseSSHArgs,
|
||||
"-3",
|
||||
"-r",
|
||||
"-i",
|
||||
"/fake/keypath/id_rsa",
|
||||
"/tmp/foo",
|
||||
"root@12.34.56.78:/home/docker/foo",
|
||||
)
|
||||
expectedCmd := exec.Command("/usr/bin/scp", expectedArgs...)
|
||||
|
||||
assert.Equal(t, expectedCmd, cmd)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestGetScpCmdWithoutSshKey(t *testing.T) {
|
||||
hostInfoLoader := MockHostInfoLoader{MockHostInfo{
|
||||
ip: "1.2.3.4",
|
||||
sshUsername: "user",
|
||||
}}
|
||||
|
||||
cmd, err := getScpCmd("/tmp/foo", "myfunhost:/home/docker/foo", true, &hostInfoLoader)
|
||||
|
||||
expectedArgs := append(
|
||||
baseSSHArgs,
|
||||
"-3",
|
||||
"-r",
|
||||
"/tmp/foo",
|
||||
"user@1.2.3.4:/home/docker/foo",
|
||||
)
|
||||
expectedCmd := exec.Command("/usr/bin/scp", expectedArgs...)
|
||||
|
||||
assert.Equal(t, expectedCmd, cmd)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/machine/libmachine"
|
||||
"github.com/docker/machine/libmachine/state"
|
||||
)
|
||||
|
||||
type errStateInvalidForSSH struct {
|
||||
HostName string
|
||||
}
|
||||
|
||||
func (e errStateInvalidForSSH) Error() string {
|
||||
return fmt.Sprintf("Error: Cannot run SSH command: Host %q is not running", e.HostName)
|
||||
}
|
||||
|
||||
func cmdSSH(c CommandLine, api libmachine.API) error {
|
||||
// Check for help flag -- Needed due to SkipFlagParsing
|
||||
firstArg := c.Args().First()
|
||||
if firstArg == "-help" || firstArg == "--help" || firstArg == "-h" {
|
||||
c.ShowHelp()
|
||||
return nil
|
||||
}
|
||||
|
||||
target, err := targetHost(c, api)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
host, err := api.Load(target)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
currentState, err := host.Driver.GetState()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if currentState != state.Running {
|
||||
return errStateInvalidForSSH{host.Name}
|
||||
}
|
||||
|
||||
client, err := host.CreateSSHClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return client.Shell(c.Args().Tail()...)
|
||||
}
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/docker/machine/commands/commandstest"
|
||||
"github.com/docker/machine/drivers/fakedriver"
|
||||
"github.com/docker/machine/libmachine"
|
||||
"github.com/docker/machine/libmachine/drivers"
|
||||
"github.com/docker/machine/libmachine/host"
|
||||
"github.com/docker/machine/libmachine/libmachinetest"
|
||||
"github.com/docker/machine/libmachine/ssh"
|
||||
"github.com/docker/machine/libmachine/ssh/sshtest"
|
||||
"github.com/docker/machine/libmachine/state"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type FakeSSHClientCreator struct {
|
||||
client ssh.Client
|
||||
}
|
||||
|
||||
func (fsc *FakeSSHClientCreator) CreateSSHClient(d drivers.Driver) (ssh.Client, error) {
|
||||
if fsc.client == nil {
|
||||
fsc.client = &sshtest.FakeClient{}
|
||||
}
|
||||
return fsc.client, nil
|
||||
}
|
||||
|
||||
func TestCmdSSH(t *testing.T) {
|
||||
testCases := []struct {
|
||||
commandLine CommandLine
|
||||
api libmachine.API
|
||||
expectedErr error
|
||||
helpShown bool
|
||||
clientCreator host.SSHClientCreator
|
||||
expectedShell []string
|
||||
}{
|
||||
{
|
||||
commandLine: &commandstest.FakeCommandLine{
|
||||
CliArgs: []string{"-h"},
|
||||
},
|
||||
api: &libmachinetest.FakeAPI{},
|
||||
expectedErr: nil,
|
||||
helpShown: true,
|
||||
},
|
||||
{
|
||||
commandLine: &commandstest.FakeCommandLine{
|
||||
CliArgs: []string{"--help"},
|
||||
},
|
||||
api: &libmachinetest.FakeAPI{},
|
||||
expectedErr: nil,
|
||||
helpShown: true,
|
||||
},
|
||||
{
|
||||
commandLine: &commandstest.FakeCommandLine{
|
||||
CliArgs: []string{},
|
||||
},
|
||||
api: &libmachinetest.FakeAPI{},
|
||||
expectedErr: ErrNoDefault,
|
||||
},
|
||||
{
|
||||
commandLine: &commandstest.FakeCommandLine{
|
||||
CliArgs: []string{"default", "df", "-h"},
|
||||
},
|
||||
api: &libmachinetest.FakeAPI{
|
||||
Hosts: []*host.Host{
|
||||
{
|
||||
Name: "default",
|
||||
Driver: &fakedriver.Driver{
|
||||
MockState: state.Running,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErr: nil,
|
||||
clientCreator: &FakeSSHClientCreator{},
|
||||
expectedShell: []string{"df", "-h"},
|
||||
},
|
||||
{
|
||||
commandLine: &commandstest.FakeCommandLine{
|
||||
CliArgs: []string{"default"},
|
||||
},
|
||||
api: &libmachinetest.FakeAPI{
|
||||
Hosts: []*host.Host{
|
||||
{
|
||||
Name: "default",
|
||||
Driver: &fakedriver.Driver{
|
||||
MockState: state.Stopped,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErr: errStateInvalidForSSH{"default"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
host.SetSSHClientCreator(tc.clientCreator)
|
||||
|
||||
err := cmdSSH(tc.commandLine, tc.api)
|
||||
assert.Equal(t, err, tc.expectedErr)
|
||||
|
||||
if fcl, ok := tc.commandLine.(*commandstest.FakeCommandLine); ok {
|
||||
assert.Equal(t, tc.helpShown, fcl.HelpShown)
|
||||
}
|
||||
|
||||
if fcc, ok := tc.clientCreator.(*FakeSSHClientCreator); ok {
|
||||
assert.Equal(t, tc.expectedShell, fcc.client.(*sshtest.FakeClient).ActivatedShell)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"github.com/docker/machine/libmachine"
|
||||
"github.com/docker/machine/libmachine/log"
|
||||
)
|
||||
|
||||
func cmdStart(c CommandLine, api libmachine.API) error {
|
||||
if err := runAction("start", c, api); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Info("Started machines may have new IP addresses. You may need to re-run the `docker-machine env` command.")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"github.com/docker/machine/libmachine"
|
||||
"github.com/docker/machine/libmachine/log"
|
||||
)
|
||||
|
||||
func cmdStatus(c CommandLine, api libmachine.API) error {
|
||||
if len(c.Args()) > 1 {
|
||||
return ErrExpectedOneMachine
|
||||
}
|
||||
|
||||
target, err := targetHost(c, api)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
host, err := api.Load(target)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
currentState, err := host.Driver.GetState()
|
||||
if err != nil {
|
||||
log.Errorf("error getting state for host %s: %s", host.Name, err)
|
||||
}
|
||||
|
||||
log.Info(currentState)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package commands
|
||||
|
||||
import "github.com/docker/machine/libmachine"
|
||||
|
||||
func cmdStop(c CommandLine, api libmachine.API) error {
|
||||
return runAction("stop", c, api)
|
||||
}
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/docker/machine/commands/commandstest"
|
||||
"github.com/docker/machine/drivers/fakedriver"
|
||||
"github.com/docker/machine/libmachine"
|
||||
"github.com/docker/machine/libmachine/host"
|
||||
"github.com/docker/machine/libmachine/libmachinetest"
|
||||
"github.com/docker/machine/libmachine/state"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCmdStop(t *testing.T) {
|
||||
testCases := []struct {
|
||||
commandLine CommandLine
|
||||
api libmachine.API
|
||||
expectedErr error
|
||||
expectedStates map[string]state.State
|
||||
}{
|
||||
{
|
||||
commandLine: &commandstest.FakeCommandLine{
|
||||
CliArgs: []string{},
|
||||
},
|
||||
api: &libmachinetest.FakeAPI{
|
||||
Hosts: []*host.Host{
|
||||
{
|
||||
Name: "default",
|
||||
Driver: &fakedriver.Driver{
|
||||
MockState: state.Running,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErr: nil,
|
||||
expectedStates: map[string]state.State{
|
||||
"default": state.Stopped,
|
||||
},
|
||||
},
|
||||
{
|
||||
commandLine: &commandstest.FakeCommandLine{
|
||||
CliArgs: []string{},
|
||||
},
|
||||
api: &libmachinetest.FakeAPI{
|
||||
Hosts: []*host.Host{
|
||||
{
|
||||
Name: "foobar",
|
||||
Driver: &fakedriver.Driver{
|
||||
MockState: state.Running,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErr: ErrNoDefault,
|
||||
expectedStates: map[string]state.State{
|
||||
"foobar": state.Running,
|
||||
},
|
||||
},
|
||||
{
|
||||
commandLine: &commandstest.FakeCommandLine{
|
||||
CliArgs: []string{"machineToStop1", "machineToStop2"},
|
||||
},
|
||||
api: &libmachinetest.FakeAPI{
|
||||
Hosts: []*host.Host{
|
||||
{
|
||||
Name: "machineToStop1",
|
||||
Driver: &fakedriver.Driver{
|
||||
MockState: state.Running,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "machineToStop2",
|
||||
Driver: &fakedriver.Driver{
|
||||
MockState: state.Running,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "machine",
|
||||
Driver: &fakedriver.Driver{
|
||||
MockState: state.Running,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErr: nil,
|
||||
expectedStates: map[string]state.State{
|
||||
"machineToStop1": state.Stopped,
|
||||
"machineToStop2": state.Stopped,
|
||||
"machine": state.Running,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
err := cmdStop(tc.commandLine, tc.api)
|
||||
assert.Equal(t, tc.expectedErr, err)
|
||||
|
||||
for hostName, expectedState := range tc.expectedStates {
|
||||
assert.Equal(t, expectedState, libmachinetest.State(tc.api, hostName))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package commands
|
||||
|
||||
import "github.com/docker/machine/libmachine"
|
||||
|
||||
func cmdUpgrade(c CommandLine, api libmachine.API) error {
|
||||
return runAction("upgrade", c, api)
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/machine/libmachine"
|
||||
)
|
||||
|
||||
func cmdURL(c CommandLine, api libmachine.API) error {
|
||||
if len(c.Args()) > 1 {
|
||||
return ErrExpectedOneMachine
|
||||
}
|
||||
|
||||
target, err := targetHost(c, api)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
host, err := api.Load(target)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
url, err := host.URL()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println(url)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/docker/machine/commands/commandstest"
|
||||
"github.com/docker/machine/drivers/fakedriver"
|
||||
"github.com/docker/machine/libmachine/host"
|
||||
"github.com/docker/machine/libmachine/libmachinetest"
|
||||
"github.com/docker/machine/libmachine/state"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCmdURLMissingMachineName(t *testing.T) {
|
||||
commandLine := &commandstest.FakeCommandLine{}
|
||||
api := &libmachinetest.FakeAPI{}
|
||||
|
||||
err := cmdURL(commandLine, api)
|
||||
|
||||
assert.Equal(t, ErrNoDefault, err)
|
||||
}
|
||||
|
||||
func TestCmdURLTooManyNames(t *testing.T) {
|
||||
commandLine := &commandstest.FakeCommandLine{
|
||||
CliArgs: []string{"machineToRemove1", "machineToRemove2"},
|
||||
}
|
||||
api := &libmachinetest.FakeAPI{}
|
||||
|
||||
err := cmdURL(commandLine, api)
|
||||
|
||||
assert.EqualError(t, err, "Error: Expected one machine name as an argument")
|
||||
}
|
||||
|
||||
func TestCmdURL(t *testing.T) {
|
||||
commandLine := &commandstest.FakeCommandLine{
|
||||
CliArgs: []string{"machine"},
|
||||
}
|
||||
api := &libmachinetest.FakeAPI{
|
||||
Hosts: []*host.Host{
|
||||
{
|
||||
Name: "machine",
|
||||
Driver: &fakedriver.Driver{
|
||||
MockState: state.Running,
|
||||
MockIP: "120.0.0.1",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
stdoutGetter := commandstest.NewStdoutGetter()
|
||||
defer stdoutGetter.Stop()
|
||||
|
||||
err := cmdURL(commandLine, api)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "tcp://120.0.0.1:2376\n", stdoutGetter.Output())
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/docker/machine/libmachine"
|
||||
"github.com/docker/machine/libmachine/mcndockerclient"
|
||||
)
|
||||
|
||||
func cmdVersion(c CommandLine, api libmachine.API) error {
|
||||
return printVersion(c, api, os.Stdout)
|
||||
}
|
||||
|
||||
func printVersion(c CommandLine, api libmachine.API, out io.Writer) error {
|
||||
if len(c.Args()) == 0 {
|
||||
c.ShowVersion()
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(c.Args()) != 1 {
|
||||
return ErrExpectedOneMachine
|
||||
}
|
||||
|
||||
host, err := api.Load(c.Args().First())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
version, err := mcndockerclient.DockerVersion(host)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintln(out, version)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"bytes"
|
||||
|
||||
"github.com/docker/machine/commands/commandstest"
|
||||
"github.com/docker/machine/libmachine/host"
|
||||
"github.com/docker/machine/libmachine/libmachinetest"
|
||||
"github.com/docker/machine/libmachine/mcndockerclient"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCmdVersion(t *testing.T) {
|
||||
commandLine := &commandstest.FakeCommandLine{}
|
||||
api := &libmachinetest.FakeAPI{}
|
||||
|
||||
err := cmdVersion(commandLine, api)
|
||||
|
||||
assert.True(t, commandLine.VersionShown)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestCmdVersionTooManyNames(t *testing.T) {
|
||||
commandLine := &commandstest.FakeCommandLine{
|
||||
CliArgs: []string{"machine1", "machine2"},
|
||||
}
|
||||
api := &libmachinetest.FakeAPI{}
|
||||
|
||||
err := cmdVersion(commandLine, api)
|
||||
|
||||
assert.EqualError(t, err, "Error: Expected one machine name as an argument")
|
||||
}
|
||||
|
||||
func TestCmdVersionNotFound(t *testing.T) {
|
||||
commandLine := &commandstest.FakeCommandLine{
|
||||
CliArgs: []string{"unknown"},
|
||||
}
|
||||
api := &libmachinetest.FakeAPI{}
|
||||
|
||||
err := cmdVersion(commandLine, api)
|
||||
|
||||
assert.EqualError(t, err, `Host does not exist: "unknown"`)
|
||||
}
|
||||
|
||||
func TestCmdVersionOnHost(t *testing.T) {
|
||||
defer func(versioner mcndockerclient.DockerVersioner) { mcndockerclient.CurrentDockerVersioner = versioner }(mcndockerclient.CurrentDockerVersioner)
|
||||
mcndockerclient.CurrentDockerVersioner = &mcndockerclient.FakeDockerVersioner{Version: "1.9.1"}
|
||||
|
||||
commandLine := &commandstest.FakeCommandLine{
|
||||
CliArgs: []string{"machine"},
|
||||
}
|
||||
api := &libmachinetest.FakeAPI{
|
||||
Hosts: []*host.Host{
|
||||
{
|
||||
Name: "machine",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
out := &bytes.Buffer{}
|
||||
err := printVersion(commandLine, api, out)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "1.9.1\n", out.String())
|
||||
}
|
||||
|
||||
func TestCmdVersionFailure(t *testing.T) {
|
||||
defer func(versioner mcndockerclient.DockerVersioner) { mcndockerclient.CurrentDockerVersioner = versioner }(mcndockerclient.CurrentDockerVersioner)
|
||||
mcndockerclient.CurrentDockerVersioner = &mcndockerclient.FakeDockerVersioner{Err: errors.New("connection failure")}
|
||||
|
||||
commandLine := &commandstest.FakeCommandLine{
|
||||
CliArgs: []string{"machine"},
|
||||
}
|
||||
api := &libmachinetest.FakeAPI{
|
||||
Hosts: []*host.Host{
|
||||
{
|
||||
Name: "machine",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
out := &bytes.Buffer{}
|
||||
err := printVersion(commandLine, api, out)
|
||||
|
||||
assert.EqualError(t, err, "connection failure")
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
!docker-machine*
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
#
|
||||
# bash prompt support for docker-machine
|
||||
#
|
||||
# This script allows you to see the active machine in your bash prompt.
|
||||
#
|
||||
# To enable:
|
||||
# 1a. Copy this file somewhere and source it in your .bashrc
|
||||
# source /some/where/docker-machine-prompt.bash
|
||||
# 1b. Alternatively, just copy this file into into /etc/bash_completion.d
|
||||
# 2. Change your PS1 to call __docker-machine-ps1 as command-substitution
|
||||
# PS1='[\u@\h \W$(__docker_machine_ps1 " [%s]")]\$ '
|
||||
#
|
||||
# Configuration:
|
||||
#
|
||||
# DOCKER_MACHINE_PS1_SHOWSTATUS
|
||||
# When set, the machine status is indicated in the prompt. This can be slow,
|
||||
# so use with care.
|
||||
#
|
||||
|
||||
__docker_machine_ps1 () {
|
||||
local format=${1:- [%s]}
|
||||
if test ${DOCKER_MACHINE_NAME}; then
|
||||
local status
|
||||
if test ${DOCKER_MACHINE_PS1_SHOWSTATUS:-false} = true; then
|
||||
status=$(docker-machine status ${DOCKER_MACHINE_NAME})
|
||||
case ${status} in
|
||||
Running)
|
||||
status=' R'
|
||||
;;
|
||||
Stopping)
|
||||
status=' R->S'
|
||||
;;
|
||||
Starting)
|
||||
status=' S->R'
|
||||
;;
|
||||
Error|Timeout)
|
||||
status=' E'
|
||||
;;
|
||||
*)
|
||||
# Just consider everything elase as 'stopped'
|
||||
status=' S'
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
printf -- "${format}" "${DOCKER_MACHINE_NAME}${status}"
|
||||
fi
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
#
|
||||
# Function wrapper to docker-machine that adds a use subcommand.
|
||||
#
|
||||
# The use subcommand runs `eval "$(docker-machine env [args])"`, which is a lot
|
||||
# less typing.
|
||||
#
|
||||
# To enable:
|
||||
# 1a. Copy this file somewhere and source it in your .bashrc
|
||||
# source /some/where/docker-machine-wrapper.bash
|
||||
# 1b. Alternatively, just copy this file into into /etc/bash_completion.d
|
||||
#
|
||||
# Configuration:
|
||||
#
|
||||
# DOCKER_MACHINE_WRAPPED
|
||||
# When set to a value other than true, this will disable the alias wrapper
|
||||
# alias for docker-machine. This is useful if you don't want the wrapper,
|
||||
# but it is installed by default by your installation.
|
||||
#
|
||||
|
||||
: ${DOCKER_MACHINE_WRAPPED:=true}
|
||||
|
||||
__docker_machine_wrapper () {
|
||||
if [[ "$1" == use ]]; then
|
||||
# Special use wrapper
|
||||
shift 1
|
||||
case "$1" in
|
||||
-h|--help|"")
|
||||
cat <<EOF
|
||||
Usage: docker-machine use [OPTIONS] [arg...]
|
||||
|
||||
Evaluate the commands to set up the environment for the Docker client
|
||||
|
||||
Description:
|
||||
Argument is a machine name.
|
||||
|
||||
Options:
|
||||
|
||||
--swarm Display the Swarm config instead of the Docker daemon
|
||||
--unset, -u Unset variables instead of setting them
|
||||
|
||||
EOF
|
||||
;;
|
||||
*)
|
||||
eval "$(docker-machine env "$@")"
|
||||
echo "Active machine: ${DOCKER_MACHINE_NAME}"
|
||||
;;
|
||||
esac
|
||||
else
|
||||
# Just call the actual docker-machine app
|
||||
command docker-machine "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
if [[ ${DOCKER_MACHINE_WRAPPED} = true ]]; then
|
||||
alias docker-machine=__docker_machine_wrapper
|
||||
fi
|
||||
|
|
@ -0,0 +1,252 @@
|
|||
#
|
||||
# bash completion file for docker-machine commands
|
||||
#
|
||||
# This script provides completion of:
|
||||
# - commands and their options
|
||||
# - machine names
|
||||
# - filepaths
|
||||
#
|
||||
# To enable the completions either:
|
||||
# - place this file in /etc/bash_completion.d
|
||||
# or
|
||||
# - copy this file to e.g. ~/.docker-machine-completion.sh and add the line
|
||||
# below to your .bashrc after bash completion features are loaded
|
||||
# . ~/.docker-machine-completion.sh
|
||||
#
|
||||
|
||||
_docker_machine_active() {
|
||||
if [[ "${cur}" == -* ]]; then
|
||||
COMPREPLY=($(compgen -W "--help" -- "${cur}"))
|
||||
else
|
||||
COMPREPLY=()
|
||||
fi
|
||||
}
|
||||
|
||||
_docker_machine_config() {
|
||||
if [[ "${cur}" == -* ]]; then
|
||||
COMPREPLY=($(compgen -W "--swarm --help" -- "${cur}"))
|
||||
else
|
||||
COMPREPLY=($(compgen -W "$(docker-machine ls -q)" -- "${cur}"))
|
||||
fi
|
||||
}
|
||||
|
||||
_docker_machine_create() {
|
||||
# cheating, b/c there are approximately one zillion options to create
|
||||
COMPREPLY=($(compgen -W "$(docker-machine create --help | grep '^ -' | sed 's/^ //; s/[^a-z0-9-].*$//')" -- "${cur}"))
|
||||
}
|
||||
|
||||
_docker_machine_env() {
|
||||
case "${prev}" in
|
||||
--shell)
|
||||
# What are the options for --shell?
|
||||
COMPREPLY=()
|
||||
;;
|
||||
*)
|
||||
if [[ "${cur}" == -* ]]; then
|
||||
COMPREPLY=($(compgen -W "--swarm --shell --unset --no-proxy --help" -- "${cur}"))
|
||||
else
|
||||
COMPREPLY=($(compgen -W "$(docker-machine ls -q)" -- "${cur}"))
|
||||
fi
|
||||
esac
|
||||
}
|
||||
|
||||
# See docker-machine-wrapper.bash for the use command
|
||||
_docker_machine_use() {
|
||||
if [[ "${cur}" == -* ]]; then
|
||||
COMPREPLY=($(compgen -W "--swarm --unset --help" -- "${cur}"))
|
||||
else
|
||||
COMPREPLY=($(compgen -W "$(docker-machine ls -q)" -- "${cur}"))
|
||||
fi
|
||||
}
|
||||
|
||||
_docker_machine_inspect() {
|
||||
case "${prev}" in
|
||||
-f|--format)
|
||||
COMPREPLY=()
|
||||
;;
|
||||
*)
|
||||
if [[ "${cur}" == -* ]]; then
|
||||
COMPREPLY=($(compgen -W "--format --help" -- "${cur}"))
|
||||
else
|
||||
COMPREPLY=($(compgen -W "$(docker-machine ls -q)" -- "${cur}"))
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_docker_machine_ip() {
|
||||
if [[ "${cur}" == -* ]]; then
|
||||
COMPREPLY=($(compgen -W "--help" -- "${cur}"))
|
||||
else
|
||||
COMPREPLY=($(compgen -W "$(docker-machine ls -q)" -- "${cur}"))
|
||||
fi
|
||||
}
|
||||
|
||||
_docker_machine_kill() {
|
||||
if [[ "${cur}" == -* ]]; then
|
||||
COMPREPLY=($(compgen -W "--help" -- "${cur}"))
|
||||
else
|
||||
COMPREPLY=($(compgen -W "$(docker-machine ls -q)" -- "${cur}"))
|
||||
fi
|
||||
}
|
||||
|
||||
_docker_machine_ls() {
|
||||
case "${prev}" in
|
||||
--filter)
|
||||
COMPREPLY=()
|
||||
;;
|
||||
*)
|
||||
COMPREPLY=($(compgen -W "--quiet --filter --format --timeout --help" -- "${cur}"))
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_docker_machine_regenerate_certs() {
|
||||
if [[ "${cur}" == -* ]]; then
|
||||
COMPREPLY=($(compgen -W "--help --force" -- "${cur}"))
|
||||
else
|
||||
COMPREPLY=($(compgen -W "$(docker-machine ls -q)" -- "${cur}"))
|
||||
fi
|
||||
}
|
||||
|
||||
_docker_machine_restart() {
|
||||
if [[ "${cur}" == -* ]]; then
|
||||
COMPREPLY=($(compgen -W "--help" -- "${cur}"))
|
||||
else
|
||||
COMPREPLY=($(compgen -W "$(docker-machine ls -q)" -- "${cur}"))
|
||||
fi
|
||||
}
|
||||
|
||||
_docker_machine_rm() {
|
||||
if [[ "${cur}" == -* ]]; then
|
||||
COMPREPLY=($(compgen -W "--help --force -y" -- "${cur}"))
|
||||
else
|
||||
# For rm, it's best to be explicit
|
||||
COMPREPLY=()
|
||||
fi
|
||||
}
|
||||
|
||||
_docker_machine_ssh() {
|
||||
if [[ "${cur}" == -* ]]; then
|
||||
COMPREPLY=($(compgen -W "--help" -- "${cur}"))
|
||||
else
|
||||
COMPREPLY=($(compgen -W "$(docker-machine ls -q)" -- "${cur}"))
|
||||
fi
|
||||
}
|
||||
|
||||
_docker_machine_scp() {
|
||||
if [[ "${cur}" == -* ]]; then
|
||||
COMPREPLY=($(compgen -W "--help --recursive" -- "${cur}"))
|
||||
else
|
||||
_filedir
|
||||
# It would be really nice to ssh to the machine and ls to complete
|
||||
# remote files.
|
||||
COMPREPLY=($(compgen -W "$(docker-machine ls -q | sed 's/$/:/')" -- "${cur}") "${COMPREPLY[@]}")
|
||||
fi
|
||||
}
|
||||
|
||||
_docker_machine_start() {
|
||||
if [[ "${cur}" == -* ]]; then
|
||||
COMPREPLY=($(compgen -W "--help" -- "${cur}"))
|
||||
else
|
||||
COMPREPLY=($(compgen -W "$(docker-machine ls -q)" -- "${cur}"))
|
||||
fi
|
||||
}
|
||||
|
||||
_docker_machine_status() {
|
||||
if [[ "${cur}" == -* ]]; then
|
||||
COMPREPLY=($(compgen -W "--help" -- "${cur}"))
|
||||
else
|
||||
COMPREPLY=($(compgen -W "$(docker-machine ls -q)" -- "${cur}"))
|
||||
fi
|
||||
}
|
||||
|
||||
_docker_machine_stop() {
|
||||
if [[ "${cur}" == -* ]]; then
|
||||
COMPREPLY=($(compgen -W "--help" -- "${cur}"))
|
||||
else
|
||||
COMPREPLY=($(compgen -W "$(docker-machine ls -q)" -- "${cur}"))
|
||||
fi
|
||||
}
|
||||
|
||||
_docker_machine_upgrade() {
|
||||
if [[ "${cur}" == -* ]]; then
|
||||
COMPREPLY=($(compgen -W "--help" -- "${cur}"))
|
||||
else
|
||||
COMPREPLY=($(compgen -W "$(docker-machine ls -q)" -- "${cur}"))
|
||||
fi
|
||||
}
|
||||
|
||||
_docker_machine_url() {
|
||||
if [[ "${cur}" == -* ]]; then
|
||||
COMPREPLY=($(compgen -W "--help" -- "${cur}"))
|
||||
else
|
||||
COMPREPLY=($(compgen -W "$(docker-machine ls -q)" -- "${cur}"))
|
||||
fi
|
||||
}
|
||||
|
||||
_docker_machine_version() {
|
||||
if [[ "${cur}" == -* ]]; then
|
||||
COMPREPLY=($(compgen -W "--help" -- "${cur}"))
|
||||
else
|
||||
COMPREPLY=($(compgen -W "$(docker-machine ls -q)" -- "${cur}"))
|
||||
fi
|
||||
}
|
||||
|
||||
_docker_machine_help() {
|
||||
if [[ "${cur}" == -* ]]; then
|
||||
COMPREPLY=($(compgen -W "--help" -- "${cur}"))
|
||||
else
|
||||
COMPREPLY=($(compgen -W "${commands[*]}" -- "${cur}"))
|
||||
fi
|
||||
}
|
||||
|
||||
_docker_machine_docker_machine() {
|
||||
if [[ " ${wants_file[*]} " =~ " ${prev} " ]]; then
|
||||
_filedir
|
||||
elif [[ " ${wants_dir[*]} " =~ " ${prev} " ]]; then
|
||||
_filedir -d
|
||||
elif [[ "${cur}" == -* ]]; then
|
||||
COMPREPLY=($(compgen -W "${flags[*]} ${wants_dir[*]} ${wants_file[*]}" -- "${cur}"))
|
||||
else
|
||||
COMPREPLY=($(compgen -W "${commands[*]}" -- "${cur}"))
|
||||
fi
|
||||
}
|
||||
|
||||
_docker_machine() {
|
||||
COMPREPLY=()
|
||||
local commands=(active config create env inspect ip kill ls regenerate-certs restart rm ssh scp start status stop upgrade url version help)
|
||||
|
||||
local flags=(--debug --native-ssh --github-api-token --bugsnag-api-token --help --version)
|
||||
local wants_dir=(--storage-path)
|
||||
local wants_file=(--tls-ca-cert --tls-ca-key --tls-client-cert --tls-client-key)
|
||||
|
||||
# Add the use subcommand, if we have an alias loaded
|
||||
if [[ ${DOCKER_MACHINE_WRAPPED} = true ]]; then
|
||||
commands=("${commands[@]}" use)
|
||||
fi
|
||||
|
||||
local cur prev words cword
|
||||
_get_comp_words_by_ref -n : cur prev words cword
|
||||
local i
|
||||
local command=docker-machine
|
||||
|
||||
for (( i=1; i < ${cword}; ++i)); do
|
||||
local word=${words[i]}
|
||||
if [[ " ${wants_file[*]} ${wants_dir[*]} " =~ " ${word} " ]]; then
|
||||
# skip the next option
|
||||
(( ++i ))
|
||||
elif [[ " ${commands[*]} " =~ " ${word} " ]]; then
|
||||
command=${word}
|
||||
fi
|
||||
done
|
||||
|
||||
local completion_func=_docker_machine_"${command//-/_}"
|
||||
if declare -F "${completion_func}" > /dev/null; then
|
||||
${completion_func}
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
complete -F _docker_machine docker-machine
|
||||
|
|
@ -0,0 +1,358 @@
|
|||
#compdef docker-machine
|
||||
# Description
|
||||
# -----------
|
||||
# zsh completion for docker-machine
|
||||
# https://github.com/leonhartX/docker-machine-zsh-completion
|
||||
# -------------------------------------------------------------------------
|
||||
# Version
|
||||
# -------
|
||||
# 0.1.1
|
||||
# -------------------------------------------------------------------------
|
||||
# Authors
|
||||
# -------
|
||||
# * Ke Xu <leonhartx.k@gmail.com>
|
||||
# -------------------------------------------------------------------------
|
||||
# Inspiration
|
||||
# -----------
|
||||
# * @sdurrheimer docker-compose-zsh-completion https://github.com/sdurrheimer/docker-compose-zsh-completion
|
||||
# * @ilkka _docker-machine
|
||||
|
||||
|
||||
__docker-machine_get_hosts() {
|
||||
[[ $PREFIX = -* ]] && return 1
|
||||
local state
|
||||
declare -a hosts
|
||||
state=$1; shift
|
||||
if [[ $state != all ]]; then
|
||||
hosts=(${(f)"$(_call_program commands docker-machine ls -q --filter state=$state)"})
|
||||
else
|
||||
hosts=(${(f)"$(_call_program commands docker-machine ls -q)"})
|
||||
fi
|
||||
_describe 'host' hosts "$@" && ret=0
|
||||
return ret
|
||||
}
|
||||
|
||||
__docker-machine_hosts_with_state() {
|
||||
declare -a hosts
|
||||
hosts=(${(f)"$(_call_program commands docker-machine ls -f '{{.Name}}\:{{.DriverName}}\({{.State}}\)\ {{.URL}}')"})
|
||||
_describe 'host' hosts
|
||||
}
|
||||
|
||||
__docker-machine_hosts_all() {
|
||||
__docker-machine_get_hosts all "$@"
|
||||
}
|
||||
|
||||
__docker-machine_hosts_running() {
|
||||
__docker-machine_get_hosts Running "$@"
|
||||
}
|
||||
|
||||
__docker-machine_get_swarm() {
|
||||
declare -a swarms
|
||||
swarms=(${(f)"$(_call_program commands docker-machine ls -f {{.Swarm}} | awk '{print $1}')"})
|
||||
_describe 'swarm' swarms
|
||||
}
|
||||
|
||||
__docker-machine_hosts_and_files() {
|
||||
_alternative "hosts:host:__docker-machine_hosts_all -qS ':'" 'files:files:_path_files'
|
||||
}
|
||||
|
||||
__docker-machine_filters() {
|
||||
[[ $PREFIX = -* ]] && return 1
|
||||
integer ret=1
|
||||
|
||||
if compset -P '*='; then
|
||||
case "${${words[-1]%=*}#*=}" in
|
||||
(driver)
|
||||
_describe -t driver-filter-opts "driver filter" opts_driver && ret=0
|
||||
;;
|
||||
(swarm)
|
||||
__docker-machine_get_swarm && ret=0
|
||||
;;
|
||||
(state)
|
||||
opts_state=('Running' 'Paused' 'Saved' 'Stopped' 'Stopping' 'Starting' 'Error')
|
||||
_describe -t state-filter-opts "state filter" opts_state && ret=0
|
||||
;;
|
||||
(name)
|
||||
__docker-machine_hosts_all && ret=0
|
||||
;;
|
||||
(label)
|
||||
_message 'label' && ret=0
|
||||
;;
|
||||
*)
|
||||
_message 'value' && ret=0
|
||||
;;
|
||||
esac
|
||||
else
|
||||
opts=('driver' 'swarm' 'state' 'name' 'label')
|
||||
_describe -t filter-opts "filter" opts -qS "=" && ret=0
|
||||
fi
|
||||
return ret
|
||||
}
|
||||
|
||||
__get_swarm_discovery() {
|
||||
declare -a masters serivces
|
||||
local service
|
||||
services=()
|
||||
masters=($(docker-machine ls -f {{.Swarm}} |grep '(master)' |awk '{print $1}'))
|
||||
for master in $masters; do
|
||||
service=${${${(f)"$(_call_program commands docker-machine inspect -f '{{.HostOptions.SwarmOptions.Discovery}}:{{.Name}}' $master)"}/:/\\:}}
|
||||
services=($services $service)
|
||||
done
|
||||
_describe -t services "swarm service" services && ret=0
|
||||
return ret
|
||||
}
|
||||
|
||||
__get_create_argument() {
|
||||
typeset -g docker_machine_driver
|
||||
if [[ CURRENT -le 2 ]]; then
|
||||
docker_machine_driver="none"
|
||||
elif [[ CURRENT > 2 && $words[CURRENT-2] = '-d' || $words[CURRENT-2] = '--driver' ]]; then
|
||||
docker_machine_driver=$words[CURRENT-1]
|
||||
elif [[ $words[CURRENT-1] =~ '^(-d|--driver)=' ]]; then
|
||||
docker_machine_driver=${${words[CURRENT-1]}/*=/}
|
||||
fi
|
||||
local driver_opt_cmd
|
||||
local -a opts_provider opts_common opts_read_argument
|
||||
opts_read_argument=(
|
||||
": :->argument"
|
||||
)
|
||||
opts_common=(
|
||||
$opts_help \
|
||||
'(--driver -d)'{--driver=,-d=}'[Driver to create machine with]:dirver:->driver-option' \
|
||||
'--engine-install-url=[Custom URL to use for engine installation]:url' \
|
||||
'*--engine-opt=[Specify arbitrary flags to include with the created engine in the form flag=value]:flag' \
|
||||
'*--engine-insecure-registry=[Specify insecure registries to allow with the created engine]:registry' \
|
||||
'*--engine-registry-mirror=[Specify registry mirrors to use]:mirror' \
|
||||
'*--engine-label=[Specify labels for the created engine]:label' \
|
||||
'--engine-storage-driver=[Specify a storage driver to use with the engine]:storage-driver:->storage-driver-option' \
|
||||
'*--engine-env=[Specify environment variables to set in the engine]:environment' \
|
||||
'--swarm[Configure Machine with Swarm]' \
|
||||
'--swarm-image=[Specify Docker image to use for Swarm]:image' \
|
||||
'--swarm-master[Configure Machine to be a Swarm master]' \
|
||||
'--swarm-discovery=[Discovery service to use with Swarm]:service:->swarm-service' \
|
||||
'--swarm-strategy=[Define a default scheduling strategy for Swarm]:strategy:(spread binpack random)' \
|
||||
'*--swarm-opt=[Define arbitrary flags for swarm]:flag' \
|
||||
'*--swarm-join-opt=[Define arbitrary flags for Swarm join]:flag' \
|
||||
'--swarm-host=[ip/socket to listen on for Swarm master]:host' \
|
||||
'--swarm-addr=[addr to advertise for Swarm (default: detect and use the machine IP)]:address' \
|
||||
'--swarm-experimental[Enable Swarm experimental features]' \
|
||||
'*--tls-san=[Support extra SANs for TLS certs]:option'
|
||||
)
|
||||
driver_opt_cmd="docker-machine create -d $docker_machine_driver | grep $docker_machine_driver | sed -e 's/\(--.*\)\ *\[\1[^]]*\]/*\1/g' -e 's/\(\[[^]]*\)/\\\\\\1\\\\/g' -e 's/\".*\"\(.*\)/\1/g' | awk '{printf \"%s[\", \$1; for(i=2;i<=NF;i++) {printf \"%s \", \$i}; print \"]\"}'"
|
||||
if [[ $docker_machine_driver != "none" ]]; then
|
||||
opts_provider=(${(f)"$(_call_program commands $driver_opt_cmd)"})
|
||||
_arguments \
|
||||
$opts_provider \
|
||||
$opts_read_argument \
|
||||
$opts_common && ret=0
|
||||
else
|
||||
_arguments $opts_common && ret=0
|
||||
fi
|
||||
case $state in
|
||||
(driver-option)
|
||||
_describe -t driver-option "driver" opts_driver && ret=0
|
||||
;;
|
||||
(storage-driver-option)
|
||||
_describe -t storage-driver-option "storage driver" opts_storage_driver && ret=0
|
||||
;;
|
||||
(swarm-service)
|
||||
__get_swarm_discovery && ret=0
|
||||
;;
|
||||
(argument)
|
||||
ret=0
|
||||
;;
|
||||
esac
|
||||
return ret
|
||||
}
|
||||
|
||||
|
||||
__docker-machine_subcommand() {
|
||||
local opts_help="(- :)"{-h,--help}"[Print usage]"
|
||||
local -a opts_only_host opts_driver opts_storage_driver opts_stragery
|
||||
opts_only_host=(
|
||||
"$opts_help"
|
||||
"*:host:__docker-machine_hosts_all"
|
||||
)
|
||||
opts_driver=('amazonec2' 'azure' 'digitalocean' 'exoscale' 'generic' 'google' 'hyperv' 'none' 'openstack' 'rackspace' 'softlayer' 'virtualbox' 'vmwarefusion' 'vmwarevcloudair' 'vmwarevsphere')
|
||||
opts_storage_driver=('overlay' 'aufs' 'btrfs' 'devicemapper' 'vfs' 'zfs')
|
||||
integer ret=1
|
||||
|
||||
case "$words[1]" in
|
||||
(active)
|
||||
_arguments \
|
||||
$opts_help \
|
||||
'(--timeout -t)'{--timeout=,-t=}'[Timeout in seconds, default to 10s]:seconds' && ret=0
|
||||
;;
|
||||
(config)
|
||||
_arguments \
|
||||
$opts_help \
|
||||
'--swarm[Display the Swarm config instead of the Docker daemon]' \
|
||||
"*:host:__docker-machine_hosts_all" && ret=0
|
||||
;;
|
||||
(create)
|
||||
__get_create_argument
|
||||
;;
|
||||
(env)
|
||||
_arguments \
|
||||
$opts_help \
|
||||
'--swarm[Display the Swarm config instead of the Docker daemon]' \
|
||||
'--shell=[Force environment to be configured for a specified shell: \[fish, cmd, powershell\], default is auto-detect]:shell' \
|
||||
'(--unset -u)'{--unset,-u}'[Unset variables instead of setting them]' \
|
||||
'--no-proxy[Add machine IP to NO_PROXY environment variable]' \
|
||||
'*:host:__docker-machine_hosts_running' && ret=0
|
||||
;;
|
||||
(help)
|
||||
_arguments ':subcommand:__docker-machine_commands' && ret=0
|
||||
;;
|
||||
(inspect)
|
||||
_arguments \
|
||||
$opts_help \
|
||||
'(--format -f)'{--format=,-f=}'[Format the output using the given go template]:template' \
|
||||
'*:host:__docker-machine_hosts_all' && ret=0
|
||||
;;
|
||||
(ip)
|
||||
_arguments \
|
||||
$opts_help \
|
||||
'*:host:__docker-machine_hosts_running' && ret=0
|
||||
;;
|
||||
(kill)
|
||||
_arguments \
|
||||
$opts_help \
|
||||
'*:host:__docker-machine_hosts_with_state' && ret=0
|
||||
;;
|
||||
(ls)
|
||||
_arguments \
|
||||
$opts_help \
|
||||
'(--quiet -q)'{--quiet,-q}'[Enable quiet mode]' \
|
||||
'*--filter=[Filter output based on conditions provided]:filter:->filter-options' \
|
||||
'(--timeout -t)'{--timeout=,-t=}'[Timeout in seconds, default to 10s]:seconds' \
|
||||
'(--format -f)'{--format=,-f=}'[Pretty-print machines using a Go template]:template' && ret=0
|
||||
case $state in
|
||||
(filter-options)
|
||||
__docker-machine_filters && ret=0
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
(provision)
|
||||
_arguments $opts_only_host && ret=0
|
||||
;;
|
||||
(regenerate-certs)
|
||||
_arguments \
|
||||
$opts_help \
|
||||
'(--force -f)'{--force,-f}'[Force rebuild and do not prompt]' \
|
||||
'*:host:__docker-machine_hosts_all' && ret=0
|
||||
;;
|
||||
(restart)
|
||||
_arguments \
|
||||
$opts_help \
|
||||
'*:host:__docker-machine_hosts_with_state' && ret=0
|
||||
;;
|
||||
(rm)
|
||||
_arguments \
|
||||
$opts_help \
|
||||
'(--force -f)'{--force,-f}'[Remove local configuration even if machine cannot be removed, also implies an automatic yes (`-y`)]' \
|
||||
'-y[Assumes automatic yes to proceed with remove, without prompting further user confirmation]' \
|
||||
'*:host:__docker-machine_hosts_with_state' && ret=0
|
||||
;;
|
||||
(scp)
|
||||
_arguments \
|
||||
$opts_help \
|
||||
'(--recursive -r)'{--recursive,-r}'[Copy files recursively (required to copy directories))]' \
|
||||
'*:files:__docker-machine_hosts_and_files' && ret=0
|
||||
;;
|
||||
(ssh)
|
||||
_arguments \
|
||||
$opts_help \
|
||||
'*:host:__docker-machine_hosts_running' && ret=0
|
||||
;;
|
||||
(start)
|
||||
_arguments \
|
||||
$opts_help \
|
||||
'*:host:__docker-machine_hosts_with_state' && ret=0
|
||||
;;
|
||||
(status)
|
||||
_arguments $opts_only_host && ret=0
|
||||
;;
|
||||
(stop)
|
||||
_arguments \
|
||||
$opts_help \
|
||||
'*:host:__docker-machine_hosts_with_state' && ret=0
|
||||
;;
|
||||
(upgrade)
|
||||
_arguments $opts_only_host && ret=0
|
||||
;;
|
||||
(url)
|
||||
_arguments \
|
||||
$opts_help \
|
||||
'*:host:__docker-machine_hosts_running' && ret=0
|
||||
;;
|
||||
esac
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
|
||||
__docker-machine_commands() {
|
||||
local cache_policy
|
||||
|
||||
zstyle -s ":completion:${curcontext}:" cache-policy cache_policy
|
||||
if [[ -z "$cache_policy" ]]; then
|
||||
zstyle ":completion:${curcontext}:" cache-policy __docker-machine_caching_policy
|
||||
fi
|
||||
|
||||
if ( [[ ${+_docker_machine_subcommands} -eq 0 ]] || _cache_invalid docker_machine_subcommands) \
|
||||
&& ! _retrieve_cache docker_machine_subcommands;
|
||||
then
|
||||
local -a lines
|
||||
lines=(${(f)"$(_call_program commands docker-machine 2>&1)"})
|
||||
_docker_machine_subcommands=(${${${lines[$((${lines[(i)Commands:]} + 1)),${lines[(I) *]}]}## #}/$'\t'##/:})
|
||||
(( $#_docker_machine_subcommands > 0 )) && _store_cache docker_machine_subcommands _docker_machine_subcommands
|
||||
fi
|
||||
_describe -t docker-machine-commands "docker-machine command" _docker_machine_subcommands
|
||||
}
|
||||
|
||||
__docker-machine_caching_policy() {
|
||||
oldp=( "$1"(Nmh+1) )
|
||||
(( $#oldp ))
|
||||
}
|
||||
|
||||
_docker-machine() {
|
||||
if [[ $service != docker-machine ]]; then
|
||||
_call_function - _$service
|
||||
return
|
||||
fi
|
||||
|
||||
local curcontext="$curcontext" state line
|
||||
integer ret=1
|
||||
typeset -A opt_args
|
||||
|
||||
_arguments -C \
|
||||
"(- :)"{-h,--help}"[Show help]" \
|
||||
"(-D --debug)"{-D,--debug}"[Enable debug mode]" \
|
||||
'(-s --stroage-path)'{-s,--storage-path}'[Configures storage path]:file:_files' \
|
||||
'--tls-ca-cert[CA to verify remotes against]:file:_files' \
|
||||
'--tls-ca-key[Private key to generate certificates]:file:_files' \
|
||||
'--tls-client-cert[Client cert to use for TLS]:file:_files' \
|
||||
'--tls-client-key[Private key used in client TLS auth]:file:_files' \
|
||||
'--github-api-token[Token to use for requests to the Github API]' \
|
||||
'--native-ssh[Use the native (Go-based) SSH implementation.]' \
|
||||
'--bugsnag-api-token[BugSnag API token for crash reporting]' \
|
||||
'(- :)'{-v,--version}'[Print the version]' \
|
||||
"(-): :->command" \
|
||||
"(-)*:: :->option-or-argument" && ret=0
|
||||
|
||||
case $state in
|
||||
(command)
|
||||
__docker-machine_commands && ret=0
|
||||
;;
|
||||
(option-or-argument)
|
||||
curcontext=${curcontext%:*:*}:docker-machine-$words[1]:
|
||||
__docker-machine_subcommand && ret=0
|
||||
ret=0
|
||||
;;
|
||||
esac
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
_docker-machine "$@"
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
// Package machine defines interfaces to manage a variety of docker instances
|
||||
// deployed on different backends (VMs, baremetal).
|
||||
// The goal is to allow users get from zero to docker as fast as possible.
|
||||
package machine
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
<!--[metadata]>
|
||||
+++
|
||||
draft = true
|
||||
title = "Machine plugins"
|
||||
description = "Machine plugins"
|
||||
keywords = ["Docker, documentation, manual, guide, reference, api"]
|
||||
+++
|
||||
<![end-metadata]-->
|
||||
|
||||
# Available driver plugins
|
||||
|
||||
This document is intended to act as a reference for the available 3rd-party
|
||||
driver plugins available in the ecosystem beyond the core Machine drivers. If
|
||||
you have created a Docker Machine driver, we highly encourage you to submit a
|
||||
pull request adding the relevant information to the list. Submitting your
|
||||
driver here will allow others to discover it and the core Machine team to keep
|
||||
you informed of upstream changes.
|
||||
|
||||
**NOTE**: The linked repositories are not maintained by or formally associated
|
||||
with Docker Inc. Use 3rd party plugins at your own risk.
|
||||
|
||||
| Name | Repository | Maintainer GitHub Handle | Maintainer Email |
|
||||
| ---------------------- | ------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
|
||||
| 1&1 Cloud Server | <https://github.com/1and1/docker-machine-driver-oneandone> | [StackPointCloud, Inc.](https://github.com/stackpointcloud) | sdk@1and1.com |
|
||||
| Aliyun ECS | <https://github.com/denverdino/docker-machine-driver-aliyunecs> | [denverdino](https://github.com/denverdino)<br/>[menglingwei](https://github.com/menglingwei) | denverdino@gmail.com<br/>v.con@qq.com |
|
||||
| Amazon Cloud Formation | <https://github.com/jeffellin/machine-cloudformation> | [Jeff Ellin](https://github.com/jeffellin) | acf@ellin.com |
|
||||
| BrightBox | <https://github.com/brightbox/docker-machine-driver-brightbox> | [NeilW](https://github.com/NeilW) | neil@aldur.co.uk |
|
||||
| CenturyLink Cloud | <https://github.com/CenturyLinkCloud/docker-machine-driver-clc> | [ack](https://github.com/ack) | albert.choi@ctl.io |
|
||||
| Citrix XenServer | <https://github.com/xenserver/docker-machine-driver-xenserver> | [robertbreker](https://github.com/robertbreker)<br>[phusl](https://github.com/phusl) | robert.breker@citrix.com<br>phus.lu@citrix.com |
|
||||
| Docker-In-Docker | <https://github.com/nathanleclaire/docker-machine-driver-dind> | [nathanleclaire](https://github.com/nathanleclaire) | nathan.leclaire@gmail.com |
|
||||
| HPE OneView | <https://github.com/HewlettPackard/docker-machine-oneview> | [wenlock](https://github.com/wenlock)<br>[miqui](https://github.com/miqui) | wenlock@hpe.com<br>miqui@hpe.com |
|
||||
| KVM | <https://github.com/dhiltgen/docker-machine-kvm> | [dhiltgen](https://github.com/dhiltgen) | daniel.hiltgen@docker.com |
|
||||
| OpenNebula | <https://github.com/OpenNebula/docker-machine-opennebula> | [jmelis](https://github.com/jmelis) | jmelis@opennebula.org |
|
||||
| OVH Cloud | <https://github.com/yadutaf/docker-machine-driver-ovh> | [yadutaf](https://github.com/yadutaf) | jt@yadutaf.fr |
|
||||
| Packet | <https://github.com/packethost/docker-machine-driver-packet> | [betawaffle](https://github.com/betawaffle) | andy@packet.net |
|
||||
| ProfitBricks | <https://github.com/profitbricks/docker-machine-driver-profitbricks> | [StackPointCloud, Inc.](https://github.com/stackpointcloud) | legal90@gmail.com |
|
||||
| Parallels Desktop for Mac | <https://github.com/Parallels/docker-machine-parallels> | [legal90](https://github.com/legal90) | legal90@gmail.com |
|
||||
| RackHD | <https://github.com/emccode/docker-machine-rackhd> | [kacole2](https://github.com/kacole2) | kendrick.coleman@emc.com |
|
||||
| SAKURA CLOUD | <https://github.com/yamamoto-febc/docker-machine-sakuracloud> | [yamamoto-febc](https://github.com/yamamoto-febc) | yamamoto.febc@gmail.com |
|
||||
| Scaleway | <https://github.com/scaleway/docker-machine-driver-scaleway> | [scaleway](https://github.com/scaleway) | opensource@scaleway.com |
|
||||
| Skytap | <https://github.com/skytap/docker-machine-driver-skytap> | [dantjones](https://github.com/dantjones) | djones@skytap.com |
|
||||
| Ubiquity Hosting | <https://github.com/ubiquityhosting/docker-machine-driver-ubiquity> | [Justin Canington](https://github.com/justacan)<br>[Andrew Ayers](https://github.com/andrew-ayers) | justin.canington@nobistech.net<br>andrew.ayers@nobistech.net |
|
||||
| UCloud | <https://github.com/ucloud/docker-machine-ucloud> | [xiaohui](https://github.com/xiaohui) | xiaohui.zju@gmail.com |
|
||||
| VMWare Workstation | <https://github.com/pecigonzalo/docker-machine-vmwareworkstation> | [pecigonzalo](https://github.com/pecigonzalo) | pecigonzalo@outlook.com |
|
||||
| VULTR | <https://github.com/janeczku/docker-machine-vultr> | [janeczku](https://github.com/janeczku) | jb@festplatte.eu.org |
|
||||
| xhyve | <https://github.com/zchee/docker-machine-driver-xhyve> | [zchee](https://github.com/zchee) | zchee.io@gmail.com |
|
||||
|
|
@ -0,0 +1,149 @@
|
|||
<!--[metadata]>
|
||||
+++
|
||||
draft=true
|
||||
title = "Docker Machine"
|
||||
description = "machine"
|
||||
keywords = ["machine, orchestration, install, installation, docker, documentation"]
|
||||
[menu.main]
|
||||
parent="mn_install"
|
||||
+++
|
||||
<![end-metadata]-->
|
||||
|
||||
# Machine Driver Specification v1
|
||||
|
||||
This is the standard configuration and specification for version 1 drivers.
|
||||
|
||||
Along with defining how a driver should provision instances, the standard
|
||||
also discusses behavior and operations Machine expects.
|
||||
|
||||
# Requirements
|
||||
|
||||
The following are required for a driver to be included as a supported driver
|
||||
for Docker Machine.
|
||||
|
||||
## Base Operating System
|
||||
|
||||
The provider must offer a base operating system supported by the Docker Engine.
|
||||
|
||||
Currently Machine requires Ubuntu for non-Boot2Docker machines. This will
|
||||
change in the future.
|
||||
|
||||
## API Access
|
||||
|
||||
We prefer accessing the provider service via HTTP APIs and strongly recommend
|
||||
using those over external executables. For example, using the Amazon EC2 API
|
||||
instead of the EC2 command line tools. If in doubt, contact a project
|
||||
maintainer.
|
||||
|
||||
## SSH
|
||||
|
||||
The provider must offer SSH access to control the instance. This does not
|
||||
have to be public, but must offer it as Machine relies on SSH for system
|
||||
level maintenance.
|
||||
|
||||
# Provider Operations
|
||||
|
||||
The following instance operations should be supported by the provider.
|
||||
|
||||
## Create
|
||||
|
||||
`Create` will launch a new instance and make sure it is ready for provisioning.
|
||||
This includes setting up the instance with the proper SSH keys and making
|
||||
sure SSH is available including any access control (firewall). This should
|
||||
return an error on failure.
|
||||
|
||||
## Remove
|
||||
|
||||
`Remove` will remove the instance from the provider. This should remove the
|
||||
instance and any associated services or artifacts that were created as part
|
||||
of the instance including keys and access groups. This should return an
|
||||
error on failure.
|
||||
|
||||
## Start
|
||||
|
||||
`Start` will start a stopped instance. This should ensure the instance is
|
||||
ready for operations such as SSH and Docker. This should return an error on
|
||||
failure.
|
||||
|
||||
## Stop
|
||||
|
||||
`Stop` will stop a running instance. This should ensure the instance is
|
||||
stopped and return an error on failure.
|
||||
|
||||
## Kill
|
||||
|
||||
`Kill` will forcibly stop a running instance. This should ensure the instance
|
||||
is stopped and return an error on failure.
|
||||
|
||||
## Restart
|
||||
|
||||
`Restart` will restart a running instance. This should ensure the instance
|
||||
is ready for operations such as SSH and Docker. This should return an error
|
||||
on failure.
|
||||
|
||||
## Status
|
||||
|
||||
`Status` will return the state of the instance. This should return the
|
||||
current state of the instance (running, stopped, error, etc). This should
|
||||
return an error on failure.
|
||||
|
||||
# Testing
|
||||
|
||||
Testing is strongly recommended for drivers. Unit tests are preferred as well
|
||||
as inclusion into the [integration tests](https://github.com/docker/machine#integration-tests).
|
||||
|
||||
# Maintaining
|
||||
|
||||
Driver plugin maintainers are encouraged to host their own repo and distribute
|
||||
the driver plugins as executables.
|
||||
|
||||
# Implementation
|
||||
|
||||
The following describes what is needed to create a Machine Driver. The driver
|
||||
interface has methods that must be implemented for all drivers. These include
|
||||
operations such as `Create`, `Remove`, `Start`, `Stop` etc.
|
||||
|
||||
For details see the [Driver Interface](https://github.com/docker/machine/blob/master/drivers/drivers.go#L24).
|
||||
|
||||
To provide this functionality, you should embed the `drivers.BaseDriver` struct, similar to the following:
|
||||
|
||||
type Driver struct {
|
||||
*drivers.BaseDriver
|
||||
DriverSpecificField string
|
||||
}
|
||||
|
||||
Each driver must then use an `init` func to "register" the driver:
|
||||
|
||||
func init() {
|
||||
drivers.Register("drivername", &drivers.RegisteredDriver{
|
||||
New: NewDriver,
|
||||
GetCreateFlags: GetCreateFlags,
|
||||
})
|
||||
}
|
||||
|
||||
## Flags
|
||||
|
||||
Driver flags are used for provider specific customizations. To add flags, use
|
||||
a `GetCreateFlags` func. For example:
|
||||
|
||||
func GetCreateFlags() []cli.Flag {
|
||||
return []cli.Flag{
|
||||
cli.StringFlag{
|
||||
EnvVar: "DRIVERNAME_TOKEN",
|
||||
Name: "drivername-token",
|
||||
Usage: "Provider access token",
|
||||
|
||||
},
|
||||
cli.StringFlag{
|
||||
EnvVar: "DRIVERNAME_IMAGE",
|
||||
Name: "drivername-image",
|
||||
Usage: "Provider Image",
|
||||
Value: "ubuntu-14-04-x64",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
## Examples
|
||||
|
||||
You can reference the existing [Drivers](https://github.com/docker/machine/tree/master/drivers)
|
||||
as well.
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
FROM docs/base:oss
|
||||
MAINTAINER Docker Docs <docs@docker.com>
|
||||
|
||||
env PROJECT=machine
|
||||
|
||||
# To get the git info for this repo
|
||||
COPY . /src
|
||||
RUN rm -rf /docs/content/$PROJECT/
|
||||
COPY . /docs/content/$PROJECT/
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
.PHONY: all default docs docs-build docs-shell shell test
|
||||
|
||||
# to allow `make DOCSDIR=docs docs-shell` (to create a bind mount in docs)
|
||||
DOCS_MOUNT := $(if $(DOCSDIR),-v $(CURDIR)/$(DOCSDIR):/$(DOCSDIR))
|
||||
|
||||
# to allow `make DOCSPORT=9000 docs`
|
||||
DOCSPORT := 8000
|
||||
|
||||
# Get the IP ADDRESS
|
||||
DOCKER_IP=$(shell python -c "import urlparse ; print urlparse.urlparse('$(DOCKER_HOST)').hostname or ''")
|
||||
HUGO_BASE_URL=$(shell test -z "$(DOCKER_IP)" && echo localhost || echo "$(DOCKER_IP)")
|
||||
HUGO_BIND_IP=0.0.0.0
|
||||
|
||||
GIT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD 2>/dev/null)
|
||||
GIT_BRANCH_CLEAN := $(shell echo $(GIT_BRANCH) | sed -e "s/[^[:alnum:]]/-/g")
|
||||
DOCKER_DOCS_IMAGE := docker-docs$(if $(GIT_BRANCH_CLEAN),:$(GIT_BRANCH_CLEAN))
|
||||
|
||||
DOCKER_RUN_DOCS := docker run --rm -it $(DOCS_MOUNT) -e AWS_S3_BUCKET -e NOCACHE
|
||||
|
||||
# for some docs workarounds (see below in "docs-build" target)
|
||||
GITCOMMIT := $(shell git rev-parse --short HEAD 2>/dev/null)
|
||||
|
||||
default: docs
|
||||
|
||||
docs: docs-build
|
||||
$(DOCKER_RUN_DOCS) -p $(if $(DOCSPORT),$(DOCSPORT):)8000 -e DOCKERHOST "$(DOCKER_DOCS_IMAGE)" hugo server --port=$(DOCSPORT) --baseUrl=$(HUGO_BASE_URL) --bind=$(HUGO_BIND_IP)
|
||||
|
||||
docs-draft: docs-build
|
||||
$(DOCKER_RUN_DOCS) -p $(if $(DOCSPORT),$(DOCSPORT):)8000 -e DOCKERHOST "$(DOCKER_DOCS_IMAGE)" hugo server --buildDrafts="true" --port=$(DOCSPORT) --baseUrl=$(HUGO_BASE_URL) --bind=$(HUGO_BIND_IP)
|
||||
|
||||
docs-shell: docs-build
|
||||
$(DOCKER_RUN_DOCS) -p $(if $(DOCSPORT),$(DOCSPORT):)8000 "$(DOCKER_DOCS_IMAGE)" bash
|
||||
|
||||
test: docs-build
|
||||
$(DOCKER_RUN_DOCS) "$(DOCKER_DOCS_IMAGE)"
|
||||
|
||||
docs-build:
|
||||
docker build -t "$(DOCKER_DOCS_IMAGE)" .
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
<!--[metadata]>
|
||||
+++
|
||||
draft = true
|
||||
title = "Machine README"
|
||||
description = "Machine README"
|
||||
keywords = ["Docker, documentation, manual, guide, reference, api"]
|
||||
+++
|
||||
<![end-metadata]-->
|
||||
|
||||
# Contributing to the Docker Machine documentation
|
||||
|
||||
The documentation in this directory is part of the [this documentation](https://docs.docker.com). Docker uses [the Hugo static generator](http://gohugo.io/overview/introduction/) to convert project Markdown files to a static HTML site.
|
||||
|
||||
You don't need to be a Hugo expert to contribute to the machine documentation. If you are familiar with Markdown, you can modify the content in the `docs` files.
|
||||
|
||||
If you want to add a new file or change the location of the document in the menu, you do need to know a little more. If you want the detail of contributing, [use our contributor guide](http://docs.docker.com/project/make-a-contribution/).
|
||||
|
||||
## Documentation contributing workflow
|
||||
|
||||
1. Edit a Markdown file in the tree.
|
||||
|
||||
2. Save your changes.
|
||||
|
||||
3. Make sure all your changes maintain an 80 character line wrap.
|
||||
|
||||
All check lines you've written. Don't wrap content you didn't change material.
|
||||
|
||||
4. Make sure you are in the `docs` subdirectory.
|
||||
|
||||
5. Build the documentation.
|
||||
|
||||
$ make docs
|
||||
---> ffcf3f6c4e97
|
||||
Removing intermediate container a676414185e8
|
||||
Successfully built ffcf3f6c4e97
|
||||
docker run --rm -it -e AWS_S3_BUCKET -e NOCACHE -p 8000:8000 -e DOCKERHOST "docs-base:test-tooling" hugo server --port=8000 --baseUrl=192.168.59.103 --bind=0.0.0.0
|
||||
ERROR: 2015/06/13 MenuEntry's .Url is deprecated and will be removed in Hugo 0.15. Use .URL instead.
|
||||
0 of 4 drafts rendered
|
||||
0 future content
|
||||
12 pages created
|
||||
0 paginator pages created
|
||||
0 tags created
|
||||
0 categories created
|
||||
in 55 ms
|
||||
Serving pages from /docs/public
|
||||
Web Server is available at http://0.0.0.0:8000/
|
||||
Press Ctrl+C to stop
|
||||
|
||||
6. Open the available server in your browser.
|
||||
|
||||
The documentation server has the complete menu but only the Docker machine
|
||||
documentation resolves. You can't access the other project docs from this
|
||||
localized build.
|
||||
|
||||
## Tips on Hugo metadata and menu positioning
|
||||
|
||||
The top of each Docker machine documentation file contains TOML metadata. The metadata is commented out to prevent it from appearing in GitHub.
|
||||
|
||||
<!--[metadata]>
|
||||
+++
|
||||
title = "Extending services in machine"
|
||||
description = "How to use Docker machine's extends keyword to share configuration between files and projects"
|
||||
keywords = ["fig, composition, machine, docker, orchestration, documentation, docs"]
|
||||
[menu.main]
|
||||
parent="workw_machine"
|
||||
weight=2
|
||||
+++
|
||||
<![end-metadata]-->
|
||||
|
||||
The metadata alone has this structure:
|
||||
|
||||
+++
|
||||
title = "Extending services in machine"
|
||||
description = "How to use Docker machine's extends keyword to share configuration between files and projects"
|
||||
keywords = ["fig, composition, machine, docker, orchestration, documentation, docs"]
|
||||
[menu.main]
|
||||
parent="workw_machine"
|
||||
weight=2
|
||||
+++
|
||||
|
||||
The `[menu.main]` section refers to navigation defined [in the main Docker menu](https://github.com/docker/docs-base/blob/hugo/config.toml). This metadata says _add a menu item called_ Extending services in machine _to the menu with the_ `smn_workdw_machine` _identifier_. If you locate the menu in the configuration, you'll find _Create multi-container applications_ is the menu title.
|
||||
|
||||
You can move an article in the tree by specifying a new parent. You can shift the location of the item by changing its weight. Higher numbers are heavier and shift the item to the bottom of menu. Low or no numbers shift it up.
|
||||
|
||||
## Other key documentation repositories
|
||||
|
||||
The `docker/docs-base` repository contains [the Hugo theme and menu configuration](https://github.com/docker/docs-base). If you open the `Dockerfile` you'll see the `make docs` relies on this as a base image for building the machine documentation.
|
||||
|
||||
The `docker/docs.docker.com` repository contains [build system for building the Docker documentation site](https://github.com/docker/docs.docker.com). Fork this repository to build the entire documentation site.
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
<!--[metadata]>
|
||||
+++
|
||||
draft=true
|
||||
+++
|
||||
<![end-metadata]-->
|
||||
|
||||
# Docker Machine Release Process
|
||||
|
||||
The Docker Machine release process is fairly straightforward and as many steps
|
||||
have been taken as possible to make it automated, but there is a procedure and
|
||||
several "checklist items" which should be documented. This document is intended
|
||||
to cover the current Docker Machine release process. It is written for Docker
|
||||
Machine core maintainers who might find themselves performing a release.
|
||||
|
||||
0. The new version of `azure` driver released in 0.7.0 is not backwards compatible
|
||||
and therefore errors out with a message saying the new driver is unsupported with
|
||||
the new version. The commit 7b961604 should be undone prior to 0.8.0 release and
|
||||
this notice must be removed from `docs/RELEASE.md`.
|
||||
1. **Get a GITHUB_TOKEN** Check that you have a proper `GITHUB_TOKEN`. This
|
||||
token needs only to have the `repo` scope. The token can be created on github
|
||||
in the settings > Personal Access Token menu.
|
||||
2. **Run the release script** At the root of the project, run the following
|
||||
command `GITHUB_TOKEN=XXXX script/release.sh X.Y.Z` where `XXXX` is the
|
||||
value of the GITHUB_TOKEN generated, `X.Y.Z` the version to release
|
||||
( Explicitly excluding the 'v' prefix, the script takes care of it.). As of
|
||||
now, this version number must match the content of `version/version.go`. The
|
||||
script has been built to be as resilient as possible, cleaning everything
|
||||
it does along its way if necessary. You can run it many times in a row,
|
||||
fixing the various bits along the way.
|
||||
3. **Update the changelog on github** -- The script generated a list of all
|
||||
commits since last release. You need to edit this manually, getting rid of
|
||||
non critical details, and putting emphasis to what need our users attention.
|
||||
4. **Update the CHANGELOG.md** -- Add the same notes from the previous step to
|
||||
the `CHANGELOG.md` file in the repository.
|
||||
5. **Update the Documentation** -- Ensure that the `docs` branch on GitHub
|
||||
(which the Docker docs team uses to deploy from) is up to date with the
|
||||
changes to be deployed from the release branch / master. Make sure to
|
||||
update `docs/install-machine.md` to have the correct version as well.
|
||||
6. **Verify the Installation** -- Copy and paste the suggested commands in the
|
||||
installation notes to ensure that they work properly. Best of all, grab an
|
||||
(uninvolved) buddy and have them try it. `docker-machine -v` should give
|
||||
them the released version once they have run the install commands.
|
||||
7. (Optional) **Drink a Glass of Wine** -- You've worked hard on this release.
|
||||
You deserve it. For wine suggestions, please consult your friendly
|
||||
neighborhood sommelier.
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
<!--[metadata]>
|
||||
+++
|
||||
title = "Command-line Completion"
|
||||
description = "Install Machine command-line completion"
|
||||
keywords = ["machine, docker, orchestration, cli, reference"]
|
||||
[menu.main]
|
||||
identifier="machine_completion"
|
||||
parent="workw_machine"
|
||||
weight=99
|
||||
+++
|
||||
<![end-metadata]-->
|
||||
|
||||
# Command-line Completion
|
||||
|
||||
Docker Machine comes with [command completion](http://en.wikipedia.org/wiki/Command-line_completion)
|
||||
for the bash and zsh shell.
|
||||
|
||||
## Installing Command Completion
|
||||
|
||||
### Bash
|
||||
|
||||
Make sure bash completion is installed. If you use a current Linux in a non-minimal installation, bash completion should be available.
|
||||
On a Mac, install with `brew install bash-completion`
|
||||
|
||||
Place the completion scripts in `/etc/bash_completion.d/` (`` `brew --prefix`/etc/bash_completion.d/`` on a Mac), using e.g.
|
||||
|
||||
files=(docker-machine docker-machine-wrapper docker-machine-prompt)
|
||||
for f in "${files[@]}"; do
|
||||
curl -L https://raw.githubusercontent.com/docker/machine/v$(docker-machine --version | tr -ds ',' ' ' | awk 'NR==1{print $(3)}')/contrib/completion/bash/$f.bash > `brew --prefix`/etc/bash_completion.d/$f
|
||||
done
|
||||
|
||||
Completion will be available upon next login.
|
||||
|
||||
|
||||
### Zsh
|
||||
|
||||
Place the completion scripts in your `/path/to/zsh/completion`, using e.g. `~/.zsh/completion/`
|
||||
|
||||
mkdir -p ~/.zsh/completion
|
||||
curl -L https://raw.githubusercontent.com/docker/machine/v$(docker-machine --version | tr -ds ',' ' ' | awk 'NR==1{print $(3)}')/contrib/completion/zsh/_docker-machine > ~/.zsh/completion/_docker-machine
|
||||
|
||||
Include the directory in your `$fpath`, e.g. by adding in `~/.zshrc`
|
||||
|
||||
fpath=(~/.zsh/completion $fpath)
|
||||
|
||||
Make sure `compinit` is loaded or do it by adding in `~/.zshrc`
|
||||
|
||||
autoload -Uz compinit && compinit -i
|
||||
|
||||
Then reload your shell
|
||||
|
||||
exec $SHELL -l
|
||||
|
||||
|
||||
<!--[metadata]>
|
||||
## Available completions
|
||||
|
||||
**TODO**
|
||||
<![end-metadata]-->
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
<!--[metadata]>
|
||||
+++
|
||||
title = "Machine concepts and help"
|
||||
description = "Understand concepts for Docker Machine, including drivers, base OS, IP addresses, environment variables"
|
||||
keywords = ["docker, machine, amazonec2, azure, digitalocean, google, openstack, rackspace, softlayer, virtualbox, vmwarefusion, vmwarevcloudair, vmwarevsphere, exoscale"]
|
||||
[menu.main]
|
||||
parent="workw_machine"
|
||||
weight=-40
|
||||
+++
|
||||
<![end-metadata]-->
|
||||
|
||||
|
||||
# Understand Machine concepts and get help
|
||||
|
||||
Docker Machine allows you to provision Docker machines in a variety of environments, including virtual machines that reside on your local system, on cloud providers, or on bare metal servers (physical computers). Docker Machine creates a Docker host, and you use the Docker Engine client as needed to build images and create containers on the host.
|
||||
|
||||
## Drivers for creating machines
|
||||
|
||||
To create a virtual machine, you supply Docker Machine with the name of the driver you want use. The driver determines where the virtual machine is created. For example, on a local Mac or Windows system, the driver is typically Oracle VirtualBox. For provisioning physical machines, a generic driver is provided. For cloud providers, Docker Machine supports drivers such as AWS, Microsoft Azure, Digital Ocean, and many more. The Docker Machine reference includes a complete [list of supported drivers](drivers/index.md).
|
||||
|
||||
## Default base operating systems for local and cloud hosts
|
||||
|
||||
Since Docker runs on Linux, each VM that Docker Machine provisions relies on a
|
||||
base operating system. For convenience, there are default base operating
|
||||
systems. For the Oracle Virtual Box driver, this base operating system is <a href="https://github.com/boot2docker/boot2docker" target="_blank">boot2docker</a>. For drivers used to connect to cloud providers, the base operating system is Ubuntu 12.04+. You can change this default when you create a machine. The Docker Machine reference includes a complete [list of
|
||||
supported operating systems](drivers/os-base.md).
|
||||
|
||||
## IP addresses for Docker hosts
|
||||
|
||||
For each machine you create, the Docker host address is the IP address of the
|
||||
Linux VM. This address is assigned by the `docker-machine create` subcommand.
|
||||
You use the `docker-machine ls` command to list the machines you have created.
|
||||
The `docker-machine ip <machine-name>` command returns a specific host's IP
|
||||
address.
|
||||
|
||||
## Configuring CLI environment variables for a Docker host
|
||||
|
||||
Before you can run a `docker` command on a machine, you need to configure your
|
||||
command-line to point to that machine. The `docker-machine env <machine-name>`
|
||||
subcommand outputs the configuration command you should use.
|
||||
|
||||
For a complete list of `docker-machine` subcommands, see the [Docker Machine subcommand reference](reference/index.md).
|
||||
|
||||
## Crash Reporting
|
||||
|
||||
Provisioning a host is a complex matter that can fail for a lot of reasons. Your
|
||||
workstation may have a wide variety of shell, network configuration, VPN, proxy
|
||||
or firewall issues. There are also reasons from the other end of the chain:
|
||||
your cloud provider or the network in between.
|
||||
|
||||
To help `docker-machine` be as stable as possible, we added a monitoring of
|
||||
crashes whenever you try to `create` or `upgrade` a host. This will send, over
|
||||
HTTPS, to Bugsnag some information about your `docker-machine` version, build,
|
||||
OS, ARCH, the path to your current shell and, the history of the last command as
|
||||
you could see it with a `--debug` option. This data is sent to help us pinpoint
|
||||
recurring issues with `docker-machine` and will only be transmitted in the case
|
||||
of a crash of `docker-machine`.
|
||||
|
||||
If you wish to opt out of error reporting, you can create a `no-error-report`
|
||||
file in your `$HOME/.docker/machine` directory, and Docker Machine will disable
|
||||
this behavior. e.g.:
|
||||
|
||||
$ mkdir -p ~/.docker/machine && touch ~/.docker/machine/no-error-report
|
||||
|
||||
Leaving the file empty is fine -- Docker Machine just checks for its presence.
|
||||
|
||||
## Getting help
|
||||
|
||||
Docker Machine is still in its infancy and under active development. If you need
|
||||
help, would like to contribute, or simply want to talk about the project with
|
||||
like-minded individuals, we have a number of open channels for communication.
|
||||
|
||||
- To report bugs or file feature requests: please use the [issue tracker on
|
||||
Github](https://github.com/docker/machine/issues).
|
||||
- To talk about the project with people in real time: please join the
|
||||
`#docker-machine` channel on IRC.
|
||||
- To contribute code or documentation changes: please [submit a pull request on
|
||||
Github](https://github.com/docker/machine/pulls).
|
||||
|
||||
For more information and resources, please visit
|
||||
[our help page](/opensource/get-help.md).
|
||||
|
||||
## Where to go next
|
||||
|
||||
- Create and run a Docker host on your [local system using VirtualBox](get-started.md)
|
||||
- Provision multiple Docker hosts [on your cloud provider](get-started-cloud.md)
|
||||
- <a href="../drivers/" target="_blank">Docker Machine driver reference</a>
|
||||
- <a href="../reference/" target="_blank">Docker Machine subcommand reference</a>
|
||||
|
|
@ -0,0 +1,168 @@
|
|||
<!--[metadata]>
|
||||
+++
|
||||
title = "Amazon Web Services"
|
||||
description = "Amazon Web Services driver for machine"
|
||||
keywords = ["machine, Amazon Web Services, driver"]
|
||||
[menu.main]
|
||||
parent="smn_machine_drivers"
|
||||
+++
|
||||
<![end-metadata]-->
|
||||
|
||||
# Amazon Web Services
|
||||
|
||||
Create machines on [Amazon Web Services](http://aws.amazon.com).
|
||||
|
||||
To create machines on [Amazon Web Services](http://aws.amazon.com), you must supply two parameters: the AWS Access Key ID and the AWS Secret Access Key.
|
||||
|
||||
## Configuring credentials
|
||||
|
||||
Before using the amazonec2 driver, ensure that you've configured credentials.
|
||||
|
||||
### AWS credential file
|
||||
|
||||
One way to configure credentials is to use the standard credential file for Amazon AWS `~/.aws/credentials` file, which might look like:
|
||||
|
||||
[default]
|
||||
aws_access_key_id = AKID1234567890
|
||||
aws_secret_access_key = MY-SECRET-KEY
|
||||
|
||||
On Mac OS or various flavors of Linux you can install the [AWS Command Line Interface](http://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html#cli-quick-configuration) (`aws cli`) in the terminal and use the `aws configure` command which guides you through the creation of the credentials file.
|
||||
|
||||
This is the simplest method, you can then create a new machine with:
|
||||
|
||||
$ docker-machine create --driver amazonec2 aws01
|
||||
|
||||
### Command line flags
|
||||
|
||||
Alternatively, you can use the flags `--amazonec2-access-key` and `--amazonec2-secret-key` on the command line:
|
||||
|
||||
$ docker-machine create --driver amazonec2 --amazonec2-access-key AKI******* --amazonec2-secret-key 8T93C******* aws01
|
||||
|
||||
### Environment variables
|
||||
|
||||
You can use environment variables:
|
||||
|
||||
$ export AWS_ACCESS_KEY_ID=AKID1234567890
|
||||
$ export AWS_SECRET_ACCESS_KEY=MY-SECRET-KEY
|
||||
$ docker-machine create --driver amazonec2 aws01
|
||||
|
||||
## Options
|
||||
|
||||
- `--amazonec2-access-key`: Your access key id for the Amazon Web Services API.
|
||||
- `--amazonec2-secret-key`: Your secret access key for the Amazon Web Services API.
|
||||
- `--amazonec2-session-token`: Your session token for the Amazon Web Services API.
|
||||
- `--amazonec2-ami`: The AMI ID of the instance to use.
|
||||
- `--amazonec2-region`: The region to use when launching the instance.
|
||||
- `--amazonec2-vpc-id`: Your VPC ID to launch the instance in.
|
||||
- `--amazonec2-zone`: The AWS zone to launch the instance in (i.e. one of a,b,c,d,e).
|
||||
- `--amazonec2-subnet-id`: AWS VPC subnet id.
|
||||
- `--amazonec2-security-group`: AWS VPC security group name.
|
||||
- `--amazonec2-tags`: AWS extra tag key-value pairs (comma-separated, e.g. key1,value1,key2,value2).
|
||||
- `--amazonec2-instance-type`: The instance type to run.
|
||||
- `--amazonec2-device-name`: The root device name of the instance.
|
||||
- `--amazonec2-root-size`: The root disk size of the instance (in GB).
|
||||
- `--amazonec2-volume-type`: The Amazon EBS volume type to be attached to the instance.
|
||||
- `--amazonec2-iam-instance-profile`: The AWS IAM role name to be used as the instance profile.
|
||||
- `--amazonec2-ssh-user`: The SSH Login username, which must match the default SSH user set in the ami used.
|
||||
- `--amazonec2-request-spot-instance`: Use spot instances.
|
||||
- `--amazonec2-spot-price`: Spot instance bid price (in dollars). Require the `--amazonec2-request-spot-instance` flag.
|
||||
- `--amazonec2-use-private-address`: Use the private IP address for docker-machine, but still create a public IP address.
|
||||
- `--amazonec2-private-address-only`: Use the private IP address only.
|
||||
- `--amazonec2-monitoring`: Enable CloudWatch Monitoring.
|
||||
- `--amazonec2-use-ebs-optimized-instance`: Create an EBS Optimized Instance, instance type must support it.
|
||||
- `--amazonec2-ssh-keypath`: Path to Private Key file to use for instance. Matching public key with .pub extension should exist
|
||||
- `--amazonec2-retries`: Set retry count for recoverable failures (use -1 to disable)
|
||||
|
||||
|
||||
#### Environment variables and default values:
|
||||
|
||||
| CLI option | Environment variable | Default |
|
||||
| ---------------------------------------- | ----------------------- | ---------------- |
|
||||
| `--amazonec2-access-key` | `AWS_ACCESS_KEY_ID` | - |
|
||||
| `--amazonec2-secret-key` | `AWS_SECRET_ACCESS_KEY` | - |
|
||||
| `--amazonec2-session-token` | `AWS_SESSION_TOKEN` | - |
|
||||
| `--amazonec2-ami` | `AWS_AMI` | `ami-5f709f34` |
|
||||
| `--amazonec2-region` | `AWS_DEFAULT_REGION` | `us-east-1` |
|
||||
| `--amazonec2-vpc-id` | `AWS_VPC_ID` | - |
|
||||
| `--amazonec2-zone` | `AWS_ZONE` | `a` |
|
||||
| `--amazonec2-subnet-id` | `AWS_SUBNET_ID` | - |
|
||||
| `--amazonec2-security-group` | `AWS_SECURITY_GROUP` | `docker-machine` |
|
||||
| `--amazonec2-tags` | `AWS_TAGS` | - |
|
||||
| `--amazonec2-instance-type` | `AWS_INSTANCE_TYPE` | `t2.micro` |
|
||||
| `--amazonec2-device-name` | `AWS_DEVICE_NAME` | `/dev/sda1` |
|
||||
| `--amazonec2-root-size` | `AWS_ROOT_SIZE` | `16` |
|
||||
| `--amazonec2-volume-type` | `AWS_VOLUME_TYPE` | `gp2` |
|
||||
| `--amazonec2-iam-instance-profile` | `AWS_INSTANCE_PROFILE` | - |
|
||||
| `--amazonec2-ssh-user` | `AWS_SSH_USER` | `ubuntu` |
|
||||
| `--amazonec2-request-spot-instance` | - | `false` |
|
||||
| `--amazonec2-spot-price` | - | `0.50` |
|
||||
| `--amazonec2-use-private-address` | - | `false` |
|
||||
| `--amazonec2-private-address-only` | - | `false` |
|
||||
| `--amazonec2-monitoring` | - | `false` |
|
||||
| `--amazonec2-use-ebs-optimized-instance` | - | `false` |
|
||||
| `--amazonec2-ssh-keypath` | `AWS_SSH_KEYPATH` | - |
|
||||
| `--amazonec2-retries` | - | `5` |
|
||||
|
||||
## Default AMIs
|
||||
|
||||
By default, the Amazon EC2 driver will use a daily image of Ubuntu 15.10.
|
||||
|
||||
| Region | AMI ID |
|
||||
| -------------- | ------------ |
|
||||
| ap-northeast-1 | ami-b36d4edd |
|
||||
| ap-southeast-1 | ami-1069af73 |
|
||||
| ap-southeast-2 | ami-1d336a7e |
|
||||
| cn-north-1 | ami-79eb2214 |
|
||||
| eu-west-1 | ami-8aa67cf9 |
|
||||
| eu-central-1 | ami-ab0210c7 |
|
||||
| sa-east-1 | ami-185de774 |
|
||||
| us-east-1 | ami-26d5af4c |
|
||||
| us-west-1 | ami-9cbcd2fc |
|
||||
| us-west-2 | ami-16b1a077 |
|
||||
| us-gov-west-1 | ami-b0bad893 |
|
||||
|
||||
## Security Group
|
||||
|
||||
Note that a security group will be created and associated to the host. This security group will have the following ports opened inbound:
|
||||
|
||||
- ssh (22/tcp)
|
||||
- docker (2376/tcp)
|
||||
- swarm (3376/tcp), only if the node is a swarm master
|
||||
|
||||
If you specify a security group yourself using the `--amazonec2-security-group` flag, the above ports will be checked and opened and the security group modified.
|
||||
If you want more ports to be opened, like application specific ports, use the aws console and modify the configuration manually.
|
||||
|
||||
## VPC ID
|
||||
|
||||
We determine your default vpc id at the start of a command.
|
||||
In some cases, either because your account does not have a default vpc, or you don't want to use the default one, you can specify a vpc with the `--amazonec2-vpc-id` flag.
|
||||
|
||||
To find the VPC ID:
|
||||
|
||||
1. Login to the AWS console
|
||||
2. Go to **Services -> VPC -> Your VPCs**.
|
||||
3. Locate the VPC ID you want from the _VPC_ column.
|
||||
4. Go to **Services -> VPC -> Subnets**. Examine the _Availability Zone_ column to verify that zone `a` exists and matches your VPC ID.
|
||||
|
||||
For example, `us-east1-a` is in the `a` availability zone. If the `a` zone is not present, you can create a new subnet in that zone or specify a different zone when you create the machine.
|
||||
|
||||
To create a machine with a non-default vpc-id:
|
||||
|
||||
$ docker-machine create --driver amazonec2 --amazonec2-access-key AKI******* --amazonec2-secret-key 8T93C********* --amazonec2-vpc-id vpc-****** aws02
|
||||
|
||||
This example assumes the VPC ID was found in the `a` availability zone. Use the`--amazonec2-zone` flag to specify a zone other than the `a` zone. For example, `--amazonec2-zone c` signifies `us-east1-c`.
|
||||
|
||||
## VPC Connectivity
|
||||
Machine uses SSH to complete the set up of instances in EC2 and requires the ability to access the instance directly.
|
||||
|
||||
If you use the flag `--amazonec2-private-address-only`, you will need to ensure that you have some method of accessing the new instance from within the internal network of the VPC (e.g. a corporate VPN to the VPC, a VPN instance inside the VPC or using Docker-machine from an instance within your VPC).
|
||||
|
||||
Configuration of VPCs is beyond the scope of this guide, however the first step in troubleshooting is ensuring if you are using private subnets that you follow the design guidance in the [AWS VPC User Guide](http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_Scenario2.html) and have some form of NAT available so that the set up process can access the internet to complete set up.
|
||||
|
||||
## Custom AMI and SSH username
|
||||
|
||||
The default SSH username for the default AMIs is `ubuntu`.
|
||||
|
||||
You need to change the SSH username only if the custom AMI you use has a different SSH username.
|
||||
|
||||
You can change the SSH username with the `--amazonec2-ssh-user` according to the AMI you selected with the `--amazonec2-ami`.
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
<!--[metadata]>
|
||||
+++
|
||||
title = "Microsoft Azure"
|
||||
description = "Microsoft Azure driver for machine"
|
||||
keywords = ["machine, Microsoft Azure, driver"]
|
||||
[menu.main]
|
||||
parent="smn_machine_drivers"
|
||||
+++
|
||||
<![end-metadata]-->
|
||||
|
||||
# Microsoft Azure
|
||||
|
||||
You will need an Azure Subscription to use this Docker Machine driver.
|
||||
[Sign up for a free trial.][trial]
|
||||
|
||||
> **NOTE:** This documentation is for the new version of the Azure driver, which started
|
||||
> shipping with v0.7.0. This driver is not backwards-compatible with the old
|
||||
> Azure driver. If you want to continue managing your existing Azure machines, please
|
||||
> download and use machine versions prior to v0.7.0.
|
||||
|
||||
[azure]: http://azure.microsoft.com/
|
||||
[trial]: https://azure.microsoft.com/free/
|
||||
|
||||
## Authentication
|
||||
|
||||
The first time you try to create a machine, Azure driver will ask you to
|
||||
authenticate:
|
||||
|
||||
$ docker-machine create --driver azure --azure-subscription-id <subs-id> <machine-name>
|
||||
Running pre-create checks...
|
||||
Microsoft Azure: To sign in, use a web browser to open the page https://aka.ms/devicelogin.
|
||||
Enter the code [...] to authenticate.
|
||||
|
||||
After authenticating, the driver will remember your credentials up to two weeks.
|
||||
|
||||
> **KNOWN ISSUE:** There is a known issue with Azure Active Directory causing stored
|
||||
> credentials to expire within hours rather than 14 days when the user logs in with
|
||||
> personal Microsoft Account (formerly _Live ID_) instead of an Active Directory account.
|
||||
> Currently, there is no ETA for resolution, however in the meanwhile you can
|
||||
> [create an AAD account][aad-docs] and login with that as a workaround.
|
||||
|
||||
[aad-docs]: https://azure.microsoft.com/documentation/articles/virtual-machines-windows-create-aad-work-id/
|
||||
|
||||
## Options
|
||||
|
||||
Azure driver only has a single required argument to make things easier. Please
|
||||
read the optional flags to configure machine details and placement further.
|
||||
|
||||
Required:
|
||||
|
||||
- `--azure-subscription-id`: **(required)** Your Azure Subscription ID.
|
||||
|
||||
Optional:
|
||||
|
||||
- `--azure-image`: Azure virtual machine image in the format of Publisher:Offer:Sku:Version [[?][vm-image]]
|
||||
- `--azure-location`: Azure region to create the virtual machine. [[?][location]]
|
||||
- `--azure-resource-group`: Azure Resource Group name to create the resources in.
|
||||
- `--azure-size`: Size for Azure Virtual Machine. [[?][vm-size]]
|
||||
- `--azure-ssh-user`: Username for SSH login.
|
||||
- `--azure-vnet`: Azure Virtual Network name to connect the virtual machine.
|
||||
[[?][vnet]] To specify a Virtual Network from another resource group, use `resourcegroup:vnet-name` format.
|
||||
- `--azure-subnet`: Azure Subnet Name to be used within the Virtual Network.
|
||||
- `--azure-subnet-prefix`: Private CIDR block. Used to create subnet if it does not exist. Must match in the case that the subnet does exist.
|
||||
- `--azure-availability-set`: Azure Availability Set to place the virtual machine into. [[?][av-set]]
|
||||
- `--azure-open-port`: Make additional port number(s) accessible from the Internet [[?][nsg]]
|
||||
- `--azure-private-ip-address`: Specify a static private IP address for the machine.
|
||||
- `--azure-use-private-ip`: Use private IP address of the machine to connect. It's useful for managing Docker machines from another machine on the same network e.g. while deploying Swarm.
|
||||
- `--azure-no-public-ip`: Do not create a public IP address for the machine (implies `--azure-use-private-ip`). Should be used only when creating machines from an Azure VM within the same subnet.
|
||||
- `--azure-static-public-ip`: Assign a static public IP address to the machine.
|
||||
- `--azure-docker-port`: Port number for Docker engine.
|
||||
- `--azure-environment`: Azure environment (e.g. `AzurePublicCloud`, `AzureChinaCloud`).
|
||||
|
||||
[vm-image]: https://azure.microsoft.com/en-us/documentation/articles/resource-groups-vm-searching/
|
||||
[location]: https://azure.microsoft.com/en-us/regions/
|
||||
[vm-size]: https://azure.microsoft.com/en-us/documentation/articles/virtual-machines-size-specs/
|
||||
[vnet]: https://azure.microsoft.com/en-us/documentation/articles/virtual-networks-overview/
|
||||
[av-set]: https://azure.microsoft.com/en-us/documentation/articles/virtual-machines-manage-availability/
|
||||
|
||||
#### Environment variables and default values
|
||||
|
||||
| CLI option | Environment variable | Default |
|
||||
| ------------------------------- | ----------------------------- | ------------------ |
|
||||
| **`--azure-subscription-id`** | `AZURE_SUBSCRIPTION_ID` | - |
|
||||
| `--azure-environment` | `AZURE_ENVIRONMENT` | `AzurePublicCloud` |
|
||||
| `--azure-image` | `AZURE_IMAGE` | `canonical:UbuntuServer:16.04.0-LTS:latest` |
|
||||
| `--azure-location` | `AZURE_LOCATION` | `westus` |
|
||||
| `--azure-resource-group` | `AZURE_RESOURCE_GROUP` | `docker-machine` |
|
||||
| `--azure-size` | `AZURE_SIZE` | `Standard_A2` |
|
||||
| `--azure-ssh-user` | `AZURE_SSH_USER` | `docker-user` |
|
||||
| `--azure-vnet` | `AZURE_VNET` | `docker-machine` |
|
||||
| `--azure-subnet` | `AZURE_SUBNET` | `docker-machine` |
|
||||
| `--azure-subnet-prefix` | `AZURE_SUBNET_PREFIX` | `192.168.0.0/16` |
|
||||
| `--azure-availability-set` | `AZURE_AVAILABILITY_SET` | `docker-machine` |
|
||||
| `--azure-open-port` | - | - |
|
||||
| `--azure-private-ip-address` | - | - |
|
||||
| `--azure-use-private-ip` | - | - |
|
||||
| `--azure-no-public-ip` | - | - |
|
||||
| `--azure-static-public-ip` | - | - |
|
||||
| `--azure-docker-port` | `AZURE_DOCKER_PORT` | `2376` |
|
||||
|
||||
## Notes
|
||||
|
||||
Azure runs fully on the new [Azure Resource Manager (ARM)][arm] stack. Each
|
||||
machine created comes with a few more Azure resources associated with it:
|
||||
|
||||
* A [Virtual Network][vnet] and a subnet under it is created to place your
|
||||
machines into. This establishes a local network between your docker machines.
|
||||
* An [Availability Set][av-set] is created to maximize availability of your
|
||||
machines.
|
||||
|
||||
These are created once when the first machine is created and reused afterwards.
|
||||
Although they are free resources, driver does a best effort to clean them up
|
||||
after the last machine using these resources is removed.
|
||||
|
||||
Each machine is created with a public dynamic IP address for external
|
||||
connectivity. All its ports (except Docker and SSH) are closed by default. You
|
||||
can use `--azure-open-port` argument to specify multiple port numbers to be
|
||||
accessible from Internet.
|
||||
|
||||
Once the machine is created, you can modify [Network Security Group][nsg]
|
||||
rules and open ports of the machine from the [Azure Portal][portal].
|
||||
|
||||
[arm]: https://azure.microsoft.com/en-us/documentation/articles/resource-group-overview/
|
||||
[nsg]: https://azure.microsoft.com/en-us/documentation/articles/virtual-networks-nsg/
|
||||
[portal]: https://portal.azure.com/
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
<!--[metadata]>
|
||||
+++
|
||||
title = "Digital Ocean"
|
||||
description = "Digital Ocean driver for machine"
|
||||
keywords = ["machine, Digital Ocean, driver"]
|
||||
[menu.main]
|
||||
parent="smn_machine_drivers"
|
||||
+++
|
||||
<![end-metadata]-->
|
||||
|
||||
# Digital Ocean
|
||||
|
||||
Create Docker machines on [Digital Ocean](https://www.digitalocean.com/).
|
||||
|
||||
You need to create a personal access token under "Apps & API" in the Digital Ocean
|
||||
Control Panel and pass that to `docker-machine create` with the `--digitalocean-access-token` option.
|
||||
|
||||
## Usage
|
||||
|
||||
$ docker-machine create --driver digitalocean --digitalocean-access-token=aa9399a2175a93b17b1c86c807e08d3fc4b79876545432a629602f61cf6ccd6b test-this
|
||||
|
||||
## Options
|
||||
|
||||
- `--digitalocean-access-token`: **required** Your personal access token for the Digital Ocean API.
|
||||
- `--digitalocean-image`: The name of the Digital Ocean image to use.
|
||||
- `--digitalocean-region`: The region to create the droplet in, see [Regions API](https://developers.digitalocean.com/documentation/v2/#regions) for how to get a list.
|
||||
- `--digitalocean-size`: The size of the Digital Ocean droplet (larger than default options are of the form `2gb`).
|
||||
- `--digitalocean-ipv6`: Enable IPv6 support for the droplet.
|
||||
- `--digitalocean-private-networking`: Enable private networking support for the droplet.
|
||||
- `--digitalocean-backups`: Enable Digital Oceans backups for the droplet.
|
||||
- `--digitalocean-userdata`: Path to file containing User Data for the droplet.
|
||||
- `--digitalocean-ssh-user`: SSH username.
|
||||
- `--digitalocean-ssh-port`: SSH port.
|
||||
- `--digitalocean-ssh-key-fingerprint`: Use an existing SSH key instead of creating a new one, see [SSH keys](https://developers.digitalocean.com/documentation/v2/#ssh-keys).
|
||||
|
||||
The DigitalOcean driver will use `ubuntu-15-10-x64` as the default image.
|
||||
|
||||
#### Environment variables and default values
|
||||
|
||||
| CLI option | Environment variable | Default |
|
||||
| ----------------------------------- | --------------------------------- | ------------------ |
|
||||
| **`--digitalocean-access-token`** | `DIGITALOCEAN_ACCESS_TOKEN` | - |
|
||||
| `--digitalocean-image` | `DIGITALOCEAN_IMAGE` | `ubuntu-15-10-x64` |
|
||||
| `--digitalocean-region` | `DIGITALOCEAN_REGION` | `nyc3` |
|
||||
| `--digitalocean-size` | `DIGITALOCEAN_SIZE` | `512mb` |
|
||||
| `--digitalocean-ipv6` | `DIGITALOCEAN_IPV6` | `false` |
|
||||
| `--digitalocean-private-networking` | `DIGITALOCEAN_PRIVATE_NETWORKING` | `false` |
|
||||
| `--digitalocean-backups` | `DIGITALOCEAN_BACKUPS` | `false` |
|
||||
| `--digitalocean-userdata` | `DIGITALOCEAN_USERDATA` | - |
|
||||
| `--digitalocean-ssh-user` | `DIGITALOCEAN_SSH_USER` | `root` |
|
||||
| `--digitalocean-ssh-port` | `DIGITALOCEAN_SSH_PORT` | 22 |
|
||||
| `--digitalocean-ssh-key-fingerprint`| `DIGITALOCEAN_SSH_KEY_FINGERPRINT`| - |
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
<!--[metadata]>
|
||||
+++
|
||||
title = "exoscale"
|
||||
description = "exoscale driver for machine"
|
||||
keywords = ["machine, exoscale, driver"]
|
||||
[menu.main]
|
||||
parent="smn_machine_drivers"
|
||||
+++
|
||||
<![end-metadata]-->
|
||||
|
||||
# Exoscale
|
||||
|
||||
Create machines on [exoscale](https://www.exoscale.ch/).
|
||||
|
||||
Get your API key and API secret key from [API details](https://portal.exoscale.ch/account/api) and pass them to `machine create` with the `--exoscale-api-key` and `--exoscale-api-secret-key` options.
|
||||
|
||||
## Usage
|
||||
|
||||
$ docker-machine create --driver exoscale --exoscale-api-key=API --exoscale-api-secret-key=SECRET vm
|
||||
|
||||
## Options
|
||||
|
||||
- `--exoscale-url`: Your API endpoint.
|
||||
- `--exoscale-api-key`: **required** Your API key.
|
||||
- `--exoscale-api-secret-key`: **required** Your API secret key.
|
||||
- `--exoscale-instance-profile`: Instance profile.
|
||||
- `--exoscale-disk-size`: Disk size for the host in GB (10, 50, 100, 200, 400).
|
||||
- `--exoscale-image`: Image template (eg. ubuntu-14.04, ubuntu-15.10).
|
||||
- `--exoscale-security-group`: Security group. It will be created if it doesn't exist.
|
||||
- `--exoscale-availability-zone`: Exoscale availability zone.
|
||||
- `--exoscale-ssh-user`: SSH username, which must match the default SSH user for the used image.
|
||||
- `--exoscale-userdata`: Path to file containing user data for cloud-init.
|
||||
|
||||
If a custom security group is provided, you need to ensure that you allow TCP ports 22 and 2376 in an ingress rule. Moreover, if you want to use Swarm, also add TCP port 3376.
|
||||
|
||||
There is a limit to the number of docker machines that an anti-affinity group can have. This can be worked around by specifying an additional anti-affinity group using `--exoscale-affinity-group=docker-machineX`
|
||||
|
||||
#### Environment variables and default values
|
||||
|
||||
| CLI option | Environment variable | Default |
|
||||
| ------------------------------- | ---------------------------- | --------------------------------- |
|
||||
| `--exoscale-url` | `EXOSCALE_ENDPOINT` | `https://api.exoscale.ch/compute` |
|
||||
| **`--exoscale-api-key`** | `EXOSCALE_API_KEY` | - |
|
||||
| **`--exoscale-api-secret-key`** | `EXOSCALE_API_SECRET` | - |
|
||||
| `--exoscale-instance-profile` | `EXOSCALE_INSTANCE_PROFILE` | `small` |
|
||||
| `--exoscale-disk-size` | `EXOSCALE_DISK_SIZE` | `50` |
|
||||
| `--exoscale-image` | `EXOSCALE_IMAGE` | `ubuntu-15.10` |
|
||||
| `--exoscale-security-group` | `EXOSCALE_SECURITY_GROUP` | `docker-machine` |
|
||||
| `--exoscale-availability-zone` | `EXOSCALE_AVAILABILITY_ZONE` | `ch-gva-2` |
|
||||
| `--exoscale-ssh-user` | `EXOSCALE_SSH_USER` | `ubuntu` |
|
||||
| `--exoscale-userdata` | `EXOSCALE_USERDATA` | - |
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
<!--[metadata]>
|
||||
+++
|
||||
title = "Google Compute Engine"
|
||||
description = "Google Compute Engine driver for machine"
|
||||
keywords = ["machine, Google Compute Engine, driver"]
|
||||
[menu.main]
|
||||
parent="smn_machine_drivers"
|
||||
+++
|
||||
<![end-metadata]-->
|
||||
|
||||
# Google Compute Engine
|
||||
|
||||
Create machines on [Google Compute Engine](https://cloud.google.com/compute/).
|
||||
You will need a Google account and a project id.
|
||||
See <https://cloud.google.com/compute/docs/projects> for details on projects.
|
||||
|
||||
### Credentials
|
||||
|
||||
The Google driver uses [Application Default Credentials](https://developers.google.com/identity/protocols/application-default-credentials)
|
||||
to get authorization credentials for use in calling Google APIs.
|
||||
|
||||
So if `docker-machine` is used from a GCE host, authentication will happen automatically
|
||||
via the built-in service account.
|
||||
Otherwise, [install gcloud](https://cloud.google.com/sdk/) and get
|
||||
through the oauth2 process with `gcloud auth login`.
|
||||
|
||||
### Example
|
||||
|
||||
To create a machine instance, specify `--driver google`, the project id and the machine name.
|
||||
|
||||
$ gcloud auth login
|
||||
$ docker-machine create --driver google --google-project PROJECT_ID vm01
|
||||
$ docker-machine create --driver google \
|
||||
--google-project PROJECT_ID \
|
||||
--google-zone us-central1-a \
|
||||
--google-machine-type f1-micro \
|
||||
vm02
|
||||
|
||||
### Options
|
||||
|
||||
- `--google-project`: **required** The id of your project to use when launching the instance.
|
||||
- `--google-zone`: The zone to launch the instance.
|
||||
- `--google-machine-type`: The type of instance.
|
||||
- `--google-machine-image`: The absolute URL to a base VM image to instantiate.
|
||||
- `--google-username`: The username to use for the instance.
|
||||
- `--google-scopes`: The scopes for OAuth 2.0 to Access Google APIs. See [Google Compute Engine Doc](https://cloud.google.com/storage/docs/authentication).
|
||||
- `--google-disk-size`: The disk size of instance.
|
||||
- `--google-disk-type`: The disk type of instance.
|
||||
- `--google-address`: Instance's static external IP (name or IP).
|
||||
- `--google-preemptible`: Instance preemptibility.
|
||||
- `--google-tags`: Instance tags (comma-separated).
|
||||
- `--google-use-internal-ip`: When this option is used during create it will make docker-machine use internal rather than public NATed IPs. The flag is persistent in the sense that a machine created with it retains the IP. It's useful for managing docker machines from another machine on the same network e.g. while deploying swarm.
|
||||
- `--google-use-internal-ip-only`: When this option is used during create, the new VM will not be assigned a public IP address. This is useful only when the host running `docker-machine` is located inside the Google Cloud infrastructure; otherwise, `docker-machine` can't reach the VM to provision the Docker daemon. The presence of this flag implies `--google-use-internal-ip`.
|
||||
- `--google-use-existing`: Don't create a new VM, use an existing one. This is useful when you'd like to provision Docker on a VM you created yourself, maybe because it uses create options not supported by this driver.
|
||||
|
||||
The GCE driver will use the `ubuntu-1510-wily-v20151114` instance image unless otherwise specified. To obtain a
|
||||
list of image URLs run:
|
||||
|
||||
gcloud compute images list --uri
|
||||
|
||||
#### Environment variables and default values
|
||||
|
||||
| CLI option | Environment variable | Default |
|
||||
| -------------------------- | ------------------------ | ------------------------------------ |
|
||||
| **`--google-project`** | `GOOGLE_PROJECT` | - |
|
||||
| `--google-zone` | `GOOGLE_ZONE` | `us-central1-a` |
|
||||
| `--google-machine-type` | `GOOGLE_MACHINE_TYPE` | `f1-standard-1` |
|
||||
| `--google-machine-image` | `GOOGLE_MACHINE_IMAGE` | `ubuntu-1510-wily-v20151114` |
|
||||
| `--google-username` | `GOOGLE_USERNAME` | `docker-user` |
|
||||
| `--google-scopes` | `GOOGLE_SCOPES` | `devstorage.read_only,logging.write` |
|
||||
| `--google-disk-size` | `GOOGLE_DISK_SIZE` | `10` |
|
||||
| `--google-disk-type` | `GOOGLE_DISK_TYPE` | `pd-standard` |
|
||||
| `--google-address` | `GOOGLE_ADDRESS` | - |
|
||||
| `--google-preemptible` | `GOOGLE_PREEMPTIBLE` | - |
|
||||
| `--google-tags` | `GOOGLE_TAGS` | - |
|
||||
| `--google-use-internal-ip` | `GOOGLE_USE_INTERNAL_IP` | - |
|
||||
| `--google-use-existing` | `GOOGLE_USE_EXISTING` | - |
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
<!--[metadata]>
|
||||
+++
|
||||
title = "Generic"
|
||||
description = "Generic driver for machine"
|
||||
keywords = ["machine, Generic, driver"]
|
||||
[menu.main]
|
||||
parent="smn_machine_drivers"
|
||||
+++
|
||||
<![end-metadata]-->
|
||||
|
||||
# Generic
|
||||
|
||||
Create machines using an existing VM/Host with SSH.
|
||||
|
||||
This is useful if you are using a provider that Machine does not support
|
||||
directly or if you would like to import an existing host to allow Docker
|
||||
Machine to manage.
|
||||
|
||||
The driver will perform a list of tasks on create:
|
||||
|
||||
- If docker is not running on the host, it will be installed automatically.
|
||||
- It will update the host packages (`apt-get update`, `yum update`...).
|
||||
- It will generate certificates to secure the docker daemon.
|
||||
- The docker daemon will be restarted, thus all running containers will be stopped.
|
||||
- The hostname will be changed to fit the machine name.
|
||||
|
||||
### Example
|
||||
|
||||
To create a machine instance, specify `--driver generic`, the IP address or DNS
|
||||
name of the host and the path to the SSH private key authorized to connect
|
||||
to the host.
|
||||
|
||||
$ docker-machine create \
|
||||
--driver generic \
|
||||
--generic-ip-address=203.0.113.81 \
|
||||
--generic-ssh-key ~/.ssh/id_rsa \
|
||||
vm
|
||||
|
||||
### Sudo privileges
|
||||
|
||||
The user that is used to SSH into the host can be specified with
|
||||
`--generic-ssh-user` flag. This user has to have password-less sudo
|
||||
privileges.
|
||||
If it's not the case, you need to edit the `sudoers` file and configure the user
|
||||
as a sudoer with `NOPASSWD`. See https://help.ubuntu.com/community/Sudoers.
|
||||
|
||||
### Options
|
||||
|
||||
- `--generic-engine-port`: Port to use for Docker Daemon (Note: This flag will not work with boot2docker).
|
||||
- `--generic-ip-address`: **required** IP Address of host.
|
||||
- `--generic-ssh-key`: Path to the SSH user private key.
|
||||
- `--generic-ssh-user`: SSH username used to connect.
|
||||
- `--generic-ssh-port`: Port to use for SSH.
|
||||
|
||||
> **Note**: You must use a base operating system supported by Machine.
|
||||
|
||||
#### Environment variables and default values
|
||||
|
||||
| CLI option | Environment variable | Default |
|
||||
| -------------------------- | -------------------- | ------------------------- |
|
||||
| `--generic-engine-port` | `GENERIC_ENGINE_PORT`| `2376` |
|
||||
| **`--generic-ip-address`** | `GENERIC_IP_ADDRESS` | - |
|
||||
| `--generic-ssh-key` | `GENERIC_SSH_KEY` | - |
|
||||
| `--generic-ssh-user` | `GENERIC_SSH_USER` | `root` |
|
||||
| `--generic-ssh-port` | `GENERIC_SSH_PORT` | `22` |
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
<!--[metadata]>
|
||||
+++
|
||||
title = "Microsoft Hyper-V"
|
||||
description = "Microsoft Hyper-V driver for machine"
|
||||
keywords = ["machine, Microsoft Hyper-V, driver"]
|
||||
[menu.main]
|
||||
parent="smn_machine_drivers"
|
||||
+++
|
||||
<![end-metadata]-->
|
||||
|
||||
# Microsoft Hyper-V
|
||||
|
||||
Creates a Boot2Docker virtual machine locally on your Windows machine
|
||||
using Hyper-V.
|
||||
|
||||
Hyper-V must be enabled on your desktop system. Docker for Windows automatically
|
||||
enables it upon install. See this article on the Microsoft developer network for
|
||||
[instructions on how to manually enable
|
||||
Hyper-V](https://msdn.microsoft.com/en-us/virtualization/hyperv_on_windows/quick_start/walkthrough_install).
|
||||
|
||||
> **Notes**:
|
||||
>
|
||||
> * You will need to use an Administrator level account to create and manage Hyper-V machines.
|
||||
>
|
||||
>* You will need an existing virtual switch to use the
|
||||
> driver. Hyper-V can share an external network interface (aka
|
||||
> bridging), see [this blog](http://blogs.technet.com/b/canitpro/archive/2014/03/11/step-by-step-enabling-hyper-v-for-use-on-windows-8-1.aspx).
|
||||
> If you would like to use NAT, create an internal network, and use
|
||||
> [Internet Connection Sharing](http://www.packet6.com/allowing-windows-8-1-hyper-v-vm-to-work-with-wifi/).
|
||||
>
|
||||
> * This reference page includes an [example](#example) that shows how to use an elelvated (Administrator-level) PowerShell and how to create and use an external network switch.
|
||||
|
||||
## Usage
|
||||
|
||||
$ docker-machine create --driver hyperv vm
|
||||
|
||||
## Options
|
||||
|
||||
- `--hyperv-boot2docker-url`: The URL of the boot2docker ISO.
|
||||
- `--hyperv-virtual-switch`: Name of the virtual switch to use.
|
||||
- `--hyperv-disk-size`: Size of disk for the host in MB.
|
||||
- `--hyperv-memory`: Size of memory for the host in MB.
|
||||
- `--hyperv-cpu-count`: Number of CPUs for the host.
|
||||
- `--hyperv-static-macaddress`: Hyper-V network adapter's static MAC address.
|
||||
- `--hyperv-vlan-id`: Hyper-V network adapter's VLAN ID if any.
|
||||
|
||||
## Environment variables and default values
|
||||
|
||||
| CLI option | Environment variable | Default |
|
||||
| ---------------------------- | -------------------------- | ------------------------ |
|
||||
| `--hyperv-boot2docker-url` | `HYPERV_BOOT2DOCKER_URL` | _Latest boot2docker url_ |
|
||||
| `--hyperv-virtual-switch` | `HYPERV_VIRTUAL_SWITCH` | _first found_ |
|
||||
| `--hyperv-disk-size` | `HYPERV_DISK_SIZE` | `20000` |
|
||||
| `--hyperv-memory` | `HYPERV_MEMORY` | `1024` |
|
||||
| `--hyperv-cpu-count` | `HYPERV_CPU_COUNT` | `1` |
|
||||
| `--hyperv-static-macaddress` | `HYPERV_STATIC_MACADDRESS` | _undefined_ |
|
||||
| `--hyperv-cpu-count` | `HYPERV_VLAN_ID` | _undefined_ |
|
||||
|
||||
## Example
|
||||
|
||||
#### 1. Make sure Hyper-V is enabled
|
||||
|
||||
Hyper-V is automatically enabled on a Docker for Windows install. To enable it manually, see [instructions on how to manually enable Hyper-V](https://msdn.microsoft.com/en-us/virtualization/hyperv_on_windows/quick_start/walkthrough_install) on the Microsoft developer network.
|
||||
|
||||
#### 2. Set up a new external network switch
|
||||
|
||||
Make sure you have Ethernet connectivity while you are doing this.
|
||||
|
||||
Open the **Hyper-V Manager**. (On Windows 10, just search for the Hyper-V Manager in the search field in the lower left search field.)
|
||||
|
||||
Select the Virtual Switch Manager on the left-side **Actions** panel.
|
||||
|
||||

|
||||
|
||||
Set up a new external network switch to use instad of DockerNAT network switch (for Moby), which is set up by default when you install Docker for Windows. (Or if you already have another network switch set up, you can use that one.)
|
||||
|
||||
For this example, we created a virtual switch called `Primary Virtual Switch`.
|
||||
|
||||

|
||||
|
||||
#### 3. Reboot
|
||||
|
||||
See [this issue on virtualbox: Hangs on Waiting for VM to start #986](https://github.com/docker/machine/issues/986).
|
||||
|
||||
A reboot of your desktop system clears out any problems with the routing tables. Without a reboot first, `docker-machine create ...` might get hung up on `Waiting for VM to start`.
|
||||
|
||||
#### 4. Create the nodes with Docker Machine and the Microsoft Hyper-V driver
|
||||
|
||||
* Start an "elevated" PowerShell (i.e., running as administrator). To do this, search for PowerShell, right-click, and choose Run as administrator.
|
||||
|
||||
* Run the `docker-machine create` commands to create machines.
|
||||
|
||||
For example, if you follow along with the [Swarm mode
|
||||
tutorial](/engine/swarm/swarm-tutorial/index.md) which asks you to create [three
|
||||
networked host
|
||||
machines](/engine/swarm/swarm-tutorial/index.md#three-networked-host-machines),
|
||||
you can create these swarm nodes: `manager1`, `worker1`, `worker2`.
|
||||
|
||||
* Use the Microsoft Hyper-V driver and reference the new virtual switch you created.
|
||||
|
||||
docker-machine create -d hyperv --hyperv-virtual-switch "<NameOfVirtualSwitch>" <nameOfNode>
|
||||
|
||||
Here is an example of creating `manager1` node:
|
||||
|
||||
```shell
|
||||
PS C:\WINDOWS\system32> docker-machine create -d hyperv --hyperv-virtual-switch "Primary Virtual Switch" manager1
|
||||
Running pre-create checks...
|
||||
Creating machine...
|
||||
(manager1) Copying C:\Users\Vicky\.docker\machine\cache\boot2docker.iso to C:\Users\Vicky\.docker\machine\machines\manag
|
||||
er1\boot2docker.iso...
|
||||
(manager1) Creating SSH key...
|
||||
(manager1) Creating VM...
|
||||
(manager1) Using switch "Primary Virtual Switch"
|
||||
(manager1) Creating VHD
|
||||
(manager1) Starting VM...
|
||||
(manager1) Waiting for host to start...
|
||||
Waiting for machine to be running, this may take a few minutes...
|
||||
Detecting operating system of created instance...
|
||||
Waiting for SSH to be available...
|
||||
Detecting the provisioner...
|
||||
Provisioning with boot2docker...
|
||||
Copying certs to the local machine directory...
|
||||
Copying certs to the remote machine...
|
||||
Setting Docker configuration on the remote daemon...
|
||||
Checking connection to Docker...
|
||||
Docker is up and running!
|
||||
To see how to connect your Docker Client to the Docker Engine running on this virtual machine, run: C:\Program Files\Doc
|
||||
ker\Docker\Resources\bin\docker-machine.exe env manager1
|
||||
PS C:\WINDOWS\system32>
|
||||
```
|
||||
|
||||
* Use the same process, driver and network switch to create the other nodes.
|
||||
|
||||
For our example, the commands will look like this:
|
||||
|
||||
```shell
|
||||
docker-machine create -d hyperv --hyperv-virtual-switch "Primary Virtual Switch" worker1
|
||||
docker-machine create -d hyperv --hyperv-virtual-switch "Primary Virtual Switch" worker2
|
||||
```
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
<!--[metadata]>
|
||||
+++
|
||||
title = "Drivers"
|
||||
description = "Reference for drivers Docker Machine supports"
|
||||
keywords = ["machine, drivers, supports"]
|
||||
[menu.main]
|
||||
parent="workw_machine"
|
||||
identifier="smn_machine_drivers"
|
||||
weight=90
|
||||
+++
|
||||
<![end-metadata]-->
|
||||
|
||||
# Supported Drivers
|
||||
|
||||
- [Amazon Web Services](aws.md)
|
||||
- [Microsoft Azure](azure.md)
|
||||
- [Digital Ocean](digital-ocean.md)
|
||||
- [Exoscale](exoscale.md)
|
||||
- [Google Compute Engine](gce.md)
|
||||
- [Generic](generic.md)
|
||||
- [Microsoft Hyper-V](hyper-v.md)
|
||||
- [OpenStack](openstack.md)
|
||||
- [Rackspace](rackspace.md)
|
||||
- [IBM Softlayer](soft-layer.md)
|
||||
- [Oracle VirtualBox](virtualbox.md)
|
||||
- [VMware vCloud Air](vm-cloud.md)
|
||||
- [VMware Fusion](vm-fusion.md)
|
||||
- [VMware vSphere](vsphere.md)
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
<!--[metadata]>
|
||||
+++
|
||||
title = "OpenStack"
|
||||
description = "OpenStack driver for machine"
|
||||
keywords = ["machine, OpenStack, driver"]
|
||||
[menu.main]
|
||||
parent="smn_machine_drivers"
|
||||
+++
|
||||
<![end-metadata]-->
|
||||
|
||||
# OpenStack
|
||||
|
||||
Create machines on [OpenStack](http://www.openstack.org/software/)
|
||||
|
||||
Mandatory:
|
||||
|
||||
- `--openstack-auth-url`: Keystone service base URL.
|
||||
- `--openstack-flavor-id` or `--openstack-flavor-name`: Identify the flavor that will be used for the machine.
|
||||
- `--openstack-image-id` or `--openstack-image-name`: Identify the image that will be used for the machine.
|
||||
|
||||
## Usage
|
||||
|
||||
$ docker-machine create --driver openstack vm
|
||||
|
||||
## Options
|
||||
|
||||
- `--openstack-active-timeout`: The timeout in seconds until the OpenStack instance must be active.
|
||||
- `--openstack-availability-zone`: The availability zone in which to launch the server.
|
||||
- `--openstack-domain-name` or `--openstack-domain-id`: Domain to use for authentication (Keystone v3 only).
|
||||
- `--openstack-endpoint-type`: Endpoint type can be `internalURL`, `adminURL` on `publicURL`. If is a helper for the driver
|
||||
to choose the right URL in the OpenStack service catalog. If not provided the default id `publicURL`
|
||||
- `--openstack-floatingip-pool`: The IP pool that will be used to get a public IP can assign it to the machine. If there is an
|
||||
IP address already allocated but not assigned to any machine, this IP will be chosen and assigned to the machine. If
|
||||
there is no IP address already allocated a new IP will be allocated and assigned to the machine.
|
||||
- `--openstack-keypair-name`: Specify the existing Nova keypair to use.
|
||||
- `--openstack-insecure`: Explicitly allow openstack driver to perform "insecure" SSL (https) requests. The server's certificate will not be verified against any certificate authorities. This option should be used with caution.
|
||||
- `--openstack-ip-version`: If the instance has both IPv4 and IPv6 address, you can select IP version. If not provided `4` will be used.
|
||||
- `--openstack-net-name` or `--openstack-net-id`: Identify the private network the machine will be connected on. If your OpenStack project project contains only one private network it will be use automatically.
|
||||
- `--openstack-password`: User password. It can be omitted if the standard environment variable `OS_PASSWORD` is set.
|
||||
- `--openstack-private-key-file`: Used with `--openstack-keypair-name`, associates the private key to the keypair.
|
||||
- `--openstack-region`: The region to work on. Can be omitted if there is only one region on the OpenStack.
|
||||
- `--openstack-sec-groups`: If security groups are available on your OpenStack you can specify a comma separated list
|
||||
to use for the machine (e.g. `secgrp001,secgrp002`).
|
||||
- `--openstack-username`: User identifier to authenticate with.
|
||||
- `--openstack-ssh-port`: Customize the SSH port if the SSH server on the machine does not listen on the default port.
|
||||
- `--openstack-ssh-user`: The username to use for SSH into the machine. If not provided `root` will be used.
|
||||
- `--openstack-tenant-name` or `--openstack-tenant-id`: Identify the tenant in which the machine will be created.
|
||||
|
||||
#### Environment variables and default values
|
||||
|
||||
| CLI option | Environment variable | Default |
|
||||
| ------------------------------- | ---------------------- | ----------- |
|
||||
| `--openstack-active-timeout` | `OS_ACTIVE_TIMEOUT` | `200` |
|
||||
| `--openstack-auth-url` | `OS_AUTH_URL` | - |
|
||||
| `--openstack-availability-zone` | `OS_AVAILABILITY_ZONE` | - |
|
||||
| `--openstack-domain-id` | `OS_DOMAIN_ID` | - |
|
||||
| `--openstack-domain-name` | `OS_DOMAIN_NAME` | - |
|
||||
| `--openstack-endpoint-type` | `OS_ENDPOINT_TYPE` | `publicURL` |
|
||||
| `--openstack-flavor-id` | `OS_FLAVOR_ID` | - |
|
||||
| `--openstack-flavor-name` | `OS_FLAVOR_NAME` | - |
|
||||
| `--openstack-floatingip-pool` | `OS_FLOATINGIP_POOL` | - |
|
||||
| `--openstack-image-id` | `OS_IMAGE_ID` | - |
|
||||
| `--openstack-image-name` | `OS_IMAGE_NAME` | - |
|
||||
| `--openstack-insecure` | `OS_INSECURE` | `false` |
|
||||
| `--openstack-ip-version` | `OS_IP_VERSION` | `4` |
|
||||
| `--openstack-keypair-name` | `OS_KEYPAIR_NAME` | - |
|
||||
| `--openstack-net-id` | `OS_NETWORK_ID` | - |
|
||||
| `--openstack-net-name` | `OS_NETWORK_NAME` | - |
|
||||
| `--openstack-password` | `OS_PASSWORD` | - |
|
||||
| `--openstack-private-key-file` | `OS_PRIVATE_KEY_FILE` | - |
|
||||
| `--openstack-region` | `OS_REGION_NAME` | - |
|
||||
| `--openstack-sec-groups` | `OS_SECURITY_GROUPS` | - |
|
||||
| `--openstack-ssh-port` | `OS_SSH_PORT` | `22` |
|
||||
| `--openstack-ssh-user` | `OS_SSH_USER` | `root` |
|
||||
| `--openstack-tenant-id` | `OS_TENANT_ID` | - |
|
||||
| `--openstack-tenant-name` | `OS_TENANT_NAME` | - |
|
||||
| `--openstack-username` | `OS_USERNAME` | - |
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
<!--[metadata]>
|
||||
+++
|
||||
title = "Driver options and operating system defaults"
|
||||
description = "Identify active machines"
|
||||
keywords = ["machine, driver, base, operating system"]
|
||||
[menu.main]
|
||||
parent="smn_machine_drivers"
|
||||
weight=-1
|
||||
+++
|
||||
<![end-metadata]-->
|
||||
|
||||
# Driver options and operating system defaults
|
||||
|
||||
When Docker Machine provisions containers on local network provider or with a
|
||||
remote, cloud provider such as Amazon Web Services, you must define both the
|
||||
driver for your provider and a base operating system. There are over 10
|
||||
supported drivers and a generic driver for adding machines for other providers.
|
||||
|
||||
Each driver has a set of options specific to that provider. These options
|
||||
provide information to machine such as connection credentials, ports, and so
|
||||
forth. For example, to create an Azure machine:
|
||||
|
||||
Grab your subscription ID from the portal, then run `docker-machine create` with
|
||||
these details:
|
||||
|
||||
```bash
|
||||
$ docker-machine create -d azure --azure-subscription-id="SUB_ID" --azure-subscription-cert="mycert.pem" A-VERY-UNIQUE-NAME
|
||||
```
|
||||
|
||||
To see a list of providers and review the options available to a provider, see
|
||||
the reference for that driver.
|
||||
|
||||
In addition to the provider, you have the option of identifying a base operating
|
||||
system. It is an option because Docker Machine has defaults for both local and
|
||||
remote providers. For local providers such as VirtualBox, Fusion, Hyper-V, and
|
||||
so forth, the default base operating system is Boot2Docker. For cloud providers,
|
||||
the base operating system is the latest Ubuntu LTS the provider supports.
|
||||
|
||||
| Operating System | Version | Notes |
|
||||
| ----------------------- | ------- | ------------------ |
|
||||
| Boot2Docker | 1.5+ | default for local |
|
||||
| Ubuntu | 12.04+ | default for remote |
|
||||
| RancherOS | 0.3+ | |
|
||||
| Debian | 8.0+ | experimental |
|
||||
| RedHat Enterprise Linux | 7.0+ | experimental |
|
||||
| CentOS | 7+ | experimental |
|
||||
| Fedora | 21+ | experimental |
|
||||
|
||||
To use a different base operating system on a remote provider, specify the
|
||||
provider's image flag and one of its available images. For example, to select a
|
||||
`debian-8-x64` image on DigitalOcean you would supply the
|
||||
`--digitalocean-image=debian-8-x64` flag.
|
||||
|
||||
If you change the base image for a provider, you may also need to change
|
||||
the SSH user. For example, the default Red Hat AMI on EC2 expects the
|
||||
SSH user to be `ec2-user`, so you would have to specify this with
|
||||
`--amazonec2-ssh-user ec2-user`.
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
<!--[metadata]>
|
||||
+++
|
||||
title = "Rackspace"
|
||||
description = "Rackspace driver for machine"
|
||||
keywords = ["machine, Rackspace, driver"]
|
||||
[menu.main]
|
||||
parent="smn_machine_drivers"
|
||||
+++
|
||||
<![end-metadata]-->
|
||||
|
||||
# Rackspace
|
||||
|
||||
Create machines on [Rackspace cloud](http://www.rackspace.com/cloud)
|
||||
|
||||
## Usage
|
||||
|
||||
$ docker-machine create --driver rackspace --rackspace-username=user --rackspace-api-key=KEY --rackspace-region=region vm
|
||||
|
||||
## Options
|
||||
|
||||
- `--rackspace-username`: **required** Rackspace account username.
|
||||
- `--rackspace-api-key`: **required** Rackspace API key.
|
||||
- `--rackspace-region`: **required** Rackspace region name.
|
||||
- `--rackspace-endpoint-type`: Rackspace endpoint type (`adminURL`, `internalURL` or the default `publicURL`).
|
||||
- `--rackspace-image-id`: Rackspace image ID. Default: Ubuntu 15.10 (Wily Werewolf) (PVHVM).
|
||||
- `--rackspace-flavor-id`: Rackspace flavor ID. Default: General Purpose 1GB.
|
||||
- `--rackspace-ssh-user`: SSH user for the newly booted machine.
|
||||
- `--rackspace-ssh-port`: SSH port for the newly booted machine.
|
||||
- `--rackspace-docker-install`: Set if Docker has to be installed on the machine.
|
||||
|
||||
The Rackspace driver will use `59a3fadd-93e7-4674-886a-64883e17115f` (Ubuntu 15.10) by default.
|
||||
|
||||
#### Environment variables and default values
|
||||
|
||||
| CLI option | Environment variable | Default |
|
||||
| ---------------------------- | -------------------- | -------------------------------------- |
|
||||
| **`--rackspace-username`** | `OS_USERNAME` | - |
|
||||
| **`--rackspace-api-key`** | `OS_API_KEY` | - |
|
||||
| **`--rackspace-region`** | `OS_REGION_NAME` | - |
|
||||
| `--rackspace-endpoint-type` | `OS_ENDPOINT_TYPE` | `publicURL` |
|
||||
| `--rackspace-image-id` | - | `59a3fadd-93e7-4674-886a-64883e17115f` |
|
||||
| `--rackspace-flavor-id` | `OS_FLAVOR_ID` | `general1-1` |
|
||||
| `--rackspace-ssh-user` | - | `root` |
|
||||
| `--rackspace-ssh-port` | - | `22` |
|
||||
| `--rackspace-docker-install` | - | `true` |
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
<!--[metadata]>
|
||||
+++
|
||||
title = "IBM Softlayer"
|
||||
description = "IBM Softlayer driver for machine"
|
||||
keywords = ["machine, IBM Softlayer, driver"]
|
||||
[menu.main]
|
||||
parent="smn_machine_drivers"
|
||||
+++
|
||||
<![end-metadata]-->
|
||||
|
||||
# IBM Softlayer
|
||||
|
||||
Create machines on [Softlayer](http://softlayer.com).
|
||||
|
||||
You need to generate an API key in the softlayer control panel.
|
||||
[Retrieve your API key](http://knowledgelayer.softlayer.com/procedure/retrieve-your-api-key)
|
||||
|
||||
## Usage
|
||||
|
||||
$ docker-machine create --driver softlayer --softlayer-user=user --softlayer-api-key=KEY --softlayer-domain=domain vm
|
||||
|
||||
## Options
|
||||
|
||||
- `--softlayer-memory`: Memory for host in MB.
|
||||
- `--softlayer-disk-size`: A value of `0` will set the SoftLayer default.
|
||||
- `--softlayer-user`: **required** Username for your SoftLayer account, api key needs to match this user.
|
||||
- `--softlayer-api-key`: **required** API key for your user account.
|
||||
- `--softlayer-region`: SoftLayer region.
|
||||
- `--softlayer-cpu`: Number of CPUs for the machine.
|
||||
- `--softlayer-hostname`: Hostname for the machine.
|
||||
- `--softlayer-domain`: **required** Domain name for the machine.
|
||||
- `--softlayer-api-endpoint`: Change SoftLayer API endpoint.
|
||||
- `--softlayer-hourly-billing`: Specifies that hourly billing should be used, otherwise monthly billing is used.
|
||||
- `--softlayer-local-disk`: Use local machine disk instead of SoftLayer SAN.
|
||||
- `--softlayer-private-net-only`: Disable public networking.
|
||||
- `--softlayer-image`: OS Image to use.
|
||||
- `--softlayer-public-vlan-id`: Your public VLAN ID.
|
||||
- `--softlayer-private-vlan-id`: Your private VLAN ID.
|
||||
|
||||
The SoftLayer driver will use `UBUNTU_LATEST` as the image type by default.
|
||||
|
||||
#### Environment variables and default values
|
||||
|
||||
| CLI option | Environment variable | Default |
|
||||
| ------------------------------ | --------------------------- | --------------------------- |
|
||||
| `--softlayer-memory` | `SOFTLAYER_MEMORY` | `1024` |
|
||||
| `--softlayer-disk-size` | `SOFTLAYER_DISK_SIZE` | `0` |
|
||||
| **`--softlayer-user`** | `SOFTLAYER_USER` | - |
|
||||
| **`--softlayer-api-key`** | `SOFTLAYER_API_KEY` | - |
|
||||
| `--softlayer-region` | `SOFTLAYER_REGION` | `dal01` |
|
||||
| `--softlayer-cpu` | `SOFTLAYER_CPU` | `1` |
|
||||
| `--softlayer-hostname` | `SOFTLAYER_HOSTNAME` | `docker` |
|
||||
| **`--softlayer-domain`** | `SOFTLAYER_DOMAIN` | - |
|
||||
| `--softlayer-api-endpoint` | `SOFTLAYER_API_ENDPOINT` | `api.softlayer.com/rest/v3` |
|
||||
| `--softlayer-hourly-billing` | `SOFTLAYER_HOURLY_BILLING` | `false` |
|
||||
| `--softlayer-local-disk` | `SOFTLAYER_LOCAL_DISK` | `false` |
|
||||
| `--softlayer-private-net-only` | `SOFTLAYER_PRIVATE_NET` | `false` |
|
||||
| `--softlayer-image` | `SOFTLAYER_IMAGE` | `UBUNTU_LATEST` |
|
||||
| `--softlayer-public-vlan-id` | `SOFTLAYER_PUBLIC_VLAN_ID` | `0` |
|
||||
| `--softlayer-private-vlan-id` | `SOFTLAYER_PRIVATE_VLAN_ID` | `0` |
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
<!--[metadata]>
|
||||
+++
|
||||
title = "Oracle VirtualBox"
|
||||
description = "Oracle VirtualBox driver for machine"
|
||||
keywords = ["machine, Oracle VirtualBox, driver"]
|
||||
[menu.main]
|
||||
parent="smn_machine_drivers"
|
||||
+++
|
||||
<![end-metadata]-->
|
||||
|
||||
# Oracle VirtualBox
|
||||
|
||||
Create machines locally using [VirtualBox](https://www.virtualbox.org/).
|
||||
This driver requires VirtualBox 5+ to be installed on your host.
|
||||
Using VirtualBox 4.3+ should work but will give you a warning. Older versions
|
||||
will refuse to work.
|
||||
|
||||
## Usage
|
||||
|
||||
$ docker-machine create --driver=virtualbox vbox-test
|
||||
|
||||
You can create an entirely new machine or you can convert a Boot2Docker VM into
|
||||
a machine by importing the VM. To convert a Boot2Docker VM, you'd use the following
|
||||
command:
|
||||
|
||||
$ docker-machine create -d virtualbox --virtualbox-import-boot2docker-vm boot2docker-vm b2d
|
||||
|
||||
The size of the VM's disk can be configured this way:
|
||||
|
||||
$ docker-machine create -d virtualbox --virtualbox-disk-size "100000" large
|
||||
|
||||
## Options
|
||||
|
||||
- `--virtualbox-memory`: Size of memory for the host in MB.
|
||||
- `--virtualbox-cpu-count`: Number of CPUs to use to create the VM. Defaults to single CPU.
|
||||
- `--virtualbox-disk-size`: Size of disk for the host in MB.
|
||||
- `--virtualbox-host-dns-resolver`: Use the host DNS resolver. (Boolean value, defaults to false)
|
||||
- `--virtualbox-boot2docker-url`: The URL of the boot2docker image. Defaults to the latest available version.
|
||||
- `--virtualbox-import-boot2docker-vm`: The name of a Boot2Docker VM to import.
|
||||
- `--virtualbox-hostonly-cidr`: The CIDR of the host only adapter.
|
||||
- `--virtualbox-hostonly-nictype`: Host Only Network Adapter Type. Possible values are are '82540EM' (Intel PRO/1000), 'Am79C973' (PCnet-FAST III) and 'virtio' Paravirtualized network adapter.
|
||||
- `--virtualbox-hostonly-nicpromisc`: Host Only Network Adapter Promiscuous Mode. Possible options are deny , allow-vms, allow-all
|
||||
- `--virtualbox-no-share`: Disable the mount of your home directory
|
||||
- `--virtualbox-no-dns-proxy`: Disable proxying all DNS requests to the host (Boolean value, default to false)
|
||||
- `--virtualbox-no-vtx-check`: Disable checking for the availability of hardware virtualization before the vm is started
|
||||
|
||||
The `--virtualbox-boot2docker-url` flag takes a few different forms. By
|
||||
default, if no value is specified for this flag, Machine will check locally for
|
||||
a boot2docker ISO. If one is found, that will be used as the ISO for the
|
||||
created machine. If one is not found, the latest ISO release available on
|
||||
[boot2docker/boot2docker](https://github.com/boot2docker/boot2docker) will be
|
||||
downloaded and stored locally for future use. Note that this means you must run
|
||||
`docker-machine upgrade` deliberately on a machine if you wish to update the "cached"
|
||||
boot2docker ISO.
|
||||
|
||||
This is the default behavior (when `--virtualbox-boot2docker-url=""`), but the
|
||||
option also supports specifying ISOs by the `http://` and `file://` protocols.
|
||||
`file://` will look at the path specified locally to locate the ISO: for
|
||||
instance, you could specify `--virtualbox-boot2docker-url
|
||||
file://$HOME/Downloads/rc.iso` to test out a release candidate ISO that you have
|
||||
downloaded already. You could also just get an ISO straight from the Internet
|
||||
using the `http://` form.
|
||||
|
||||
To customize the host only adapter, you can use the `--virtualbox-hostonly-cidr`
|
||||
flag. This will specify the host IP and Machine will calculate the VirtualBox
|
||||
DHCP server address (a random IP on the subnet between `.1` and `.25`) so
|
||||
it does not clash with the specified host IP.
|
||||
Machine will also specify the DHCP lower bound to `.100` and the upper bound
|
||||
to `.254`. For example, a specified CIDR of `192.168.24.1/24` would have a
|
||||
DHCP server between `192.168.24.2-25`, a lower bound of `192.168.24.100` and
|
||||
upper bound of `192.168.24.254`.
|
||||
|
||||
#### Environment variables and default values
|
||||
|
||||
| CLI option | Environment variable | Default |
|
||||
| ------------------------------------ | ---------------------------------- | ------------------------ |
|
||||
| `--virtualbox-memory` | `VIRTUALBOX_MEMORY_SIZE` | `1024` |
|
||||
| `--virtualbox-cpu-count` | `VIRTUALBOX_CPU_COUNT` | `1` |
|
||||
| `--virtualbox-disk-size` | `VIRTUALBOX_DISK_SIZE` | `20000` |
|
||||
| `--virtualbox-host-dns-resolver` | `VIRTUALBOX_HOST_DNS_RESOLVER` | `false` |
|
||||
| `--virtualbox-boot2docker-url` | `VIRTUALBOX_BOOT2DOCKER_URL` | _Latest boot2docker url_ |
|
||||
| `--virtualbox-import-boot2docker-vm` | `VIRTUALBOX_BOOT2DOCKER_IMPORT_VM` | `boot2docker-vm` |
|
||||
| `--virtualbox-hostonly-cidr` | `VIRTUALBOX_HOSTONLY_CIDR` | `192.168.99.1/24` |
|
||||
| `--virtualbox-hostonly-nictype` | `VIRTUALBOX_HOSTONLY_NIC_TYPE` | `82540EM` |
|
||||
| `--virtualbox-hostonly-nicpromisc` | `VIRTUALBOX_HOSTONLY_NIC_PROMISC` | `deny` |
|
||||
| `--virtualbox-no-share` | `VIRTUALBOX_NO_SHARE` | `false` |
|
||||
| `--virtualbox-no-dns-proxy` | `VIRTUALBOX_NO_DNS_PROXY` | `false` |
|
||||
| `--virtualbox-no-vtx-check` | `VIRTUALBOX_NO_VTX_CHECK` | `false` |
|
||||
|
||||
## Known Issues
|
||||
|
||||
Vboxfs suffers from a [longstanding bug](https://www.virtualbox.org/ticket/9069)
|
||||
causing [sendfile(2)](http://linux.die.net/man/2/sendfile) to serve cached file
|
||||
contents.
|
||||
|
||||
This will often cause problems when using a web server such as nginx to serve
|
||||
static files from a shared volume. For development environments, a good
|
||||
workaround is to disable sendfile in your server configuration.
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
<!--[metadata]>
|
||||
+++
|
||||
title = "VMware vCloud Air"
|
||||
description = "VMware vCloud Air driver for machine"
|
||||
keywords = ["machine, VMware vCloud Air, driver"]
|
||||
[menu.main]
|
||||
parent="smn_machine_drivers"
|
||||
+++
|
||||
<![end-metadata]-->
|
||||
|
||||
# VMware vCloud Air
|
||||
|
||||
Creates machines on [vCloud Air](http://vcloud.vmware.com) subscription service. You need an account within an existing subscription of vCloud Air VPC or Dedicated Cloud.
|
||||
|
||||
## Usage
|
||||
|
||||
$ docker-machine create --driver vmwarevcloudair --vmwarevcloudair-username=user --vmwarevcloudair-password=SECRET vm
|
||||
|
||||
## Options
|
||||
|
||||
- `--vmwarevcloudair-username`: **required** vCloud Air Username.
|
||||
- `--vmwarevcloudair-password`: **required** vCloud Air Password.
|
||||
- `--vmwarevcloudair-computeid`: Compute ID (if using Dedicated Cloud).
|
||||
- `--vmwarevcloudair-vdcid`: Virtual Data Center ID.
|
||||
- `--vmwarevcloudair-orgvdcnetwork`: Organization VDC Network to attach.
|
||||
- `--vmwarevcloudair-edgegateway`: Organization Edge Gateway.
|
||||
- `--vmwarevcloudair-publicip`: Org Public IP to use.
|
||||
- `--vmwarevcloudair-catalog`: Catalog.
|
||||
- `--vmwarevcloudair-catalogitem`: Catalog Item.
|
||||
- `--vmwarevcloudair-provision`: Install Docker binaries.
|
||||
- `--vmwarevcloudair-cpu-count`: VM CPU Count.
|
||||
- `--vmwarevcloudair-memory-size`: VM Memory Size in MB.
|
||||
- `--vmwarevcloudair-ssh-port`: SSH port.
|
||||
- `--vmwarevcloudair-docker-port`: Docker port.
|
||||
|
||||
The VMware vCloud Air driver will use the `Ubuntu Server 12.04 LTS (amd64 20140927)` image by default.
|
||||
|
||||
#### Environment variables and default values
|
||||
|
||||
| CLI option | Environment variable | Default |
|
||||
| --------------------------------- | ------------------------- | ------------------------------------------ |
|
||||
| **`--vmwarevcloudair-username`** | `VCLOUDAIR_USERNAME` | - |
|
||||
| **`--vmwarevcloudair-password`** | `VCLOUDAIR_PASSWORD` | - |
|
||||
| `--vmwarevcloudair-computeid` | `VCLOUDAIR_COMPUTEID` | - |
|
||||
| `--vmwarevcloudair-vdcid` | `VCLOUDAIR_VDCID` | - |
|
||||
| `--vmwarevcloudair-orgvdcnetwork` | `VCLOUDAIR_ORGVDCNETWORK` | `<vdcid>-default-routed` |
|
||||
| `--vmwarevcloudair-edgegateway` | `VCLOUDAIR_EDGEGATEWAY` | `<vdcid>` |
|
||||
| `--vmwarevcloudair-publicip` | `VCLOUDAIR_PUBLICIP` | - |
|
||||
| `--vmwarevcloudair-catalog` | `VCLOUDAIR_CATALOG` | `Public Catalog` |
|
||||
| `--vmwarevcloudair-catalogitem` | `VCLOUDAIR_CATALOGITEM` | `Ubuntu Server 12.04 LTS (amd64 20140927)` |
|
||||
| `--vmwarevcloudair-provision` | `VCLOUDAIR_PROVISION` | `true` |
|
||||
| `--vmwarevcloudair-cpu-count` | `VCLOUDAIR_CPU_COUNT` | `1` |
|
||||
| `--vmwarevcloudair-memory-size` | `VCLOUDAIR_MEMORY_SIZE` | `2048` |
|
||||
| `--vmwarevcloudair-ssh-port` | `VCLOUDAIR_SSH_PORT` | `22` |
|
||||
| `--vmwarevcloudair-docker-port` | `VCLOUDAIR_DOCKER_PORT` | `2376` |
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
<!--[metadata]>
|
||||
+++
|
||||
title = "VMware Fusion"
|
||||
description = "VMware Fusion driver for machine"
|
||||
keywords = ["machine, VMware Fusion, driver"]
|
||||
[menu.main]
|
||||
parent="smn_machine_drivers"
|
||||
+++
|
||||
<![end-metadata]-->
|
||||
|
||||
# VMware Fusion
|
||||
|
||||
Creates machines locally on [VMware Fusion](http://www.vmware.com/products/fusion). Requires VMware Fusion to be installed.
|
||||
|
||||
## Usage
|
||||
|
||||
$ docker-machine create --driver vmwarefusion vm
|
||||
|
||||
## Options
|
||||
|
||||
- `--vmwarefusion-boot2docker-url`: URL for boot2docker image.
|
||||
- `--vmwarefusion-cpu-count`: Number of CPUs for the machine (-1 to use the number of CPUs available)
|
||||
- `--vmwarefusion-disk-size`: Size of disk for host VM (in MB).
|
||||
- `--vmwarefusion-memory-size`: Size of memory for host VM (in MB).
|
||||
- `--vmwarefusion-no-share`: Disable the mount of your home directory.
|
||||
|
||||
The VMware Fusion driver uses the latest boot2docker image.
|
||||
See [frapposelli/boot2docker](https://github.com/frapposelli/boot2docker/tree/vmware-64bit)
|
||||
|
||||
#### Environment variables and default values
|
||||
|
||||
| CLI option | Environment variable | Default |
|
||||
| -------------------------------- | ------------------------ | ------------------------ |
|
||||
| `--vmwarefusion-boot2docker-url` | `FUSION_BOOT2DOCKER_URL` | _Latest boot2docker url_ |
|
||||
| `--vmwarefusion-cpu-count` | `FUSION_CPU_COUNT` | `1` |
|
||||
| `--vmwarefusion-disk-size` | `FUSION_DISK_SIZE` | `20000` |
|
||||
| `--vmwarefusion-memory-size` | `FUSION_MEMORY_SIZE` | `1024` |
|
||||
| `--vmwarefusion-no-share` | `FUSION_NO_SHARE` | `false` |
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
<!--[metadata]>
|
||||
+++
|
||||
title = "VMware vSphere"
|
||||
description = "VMware vSphere driver for machine"
|
||||
keywords = ["machine, VMware vSphere, driver"]
|
||||
[menu.main]
|
||||
parent="smn_machine_drivers"
|
||||
+++
|
||||
<![end-metadata]-->
|
||||
|
||||
# VMware vSphere
|
||||
|
||||
Creates machines on a [VMware vSphere](http://www.vmware.com/products/vsphere) Virtual Infrastructure. The machine must have a working vSphere ESXi installation. You can use a paid license or free 60 day trial license. Your installation may also include an optional VCenter server.
|
||||
|
||||
## Usage
|
||||
|
||||
$ docker-machine create --driver vmwarevsphere --vmwarevsphere-username=user --vmwarevsphere-password=SECRET vm
|
||||
|
||||
## Options
|
||||
|
||||
- `--vmwarevsphere-username`: **required** vSphere Username.
|
||||
- `--vmwarevsphere-password`: **required** vSphere Password.
|
||||
- `--vmwarevsphere-cpu-count`: CPU number for Docker VM.
|
||||
- `--vmwarevsphere-memory-size`: Size of memory for Docker VM (in MB).
|
||||
- `--vmwarevsphere-disk-size`: Size of disk for Docker VM (in MB).
|
||||
- `--vmwarevsphere-boot2docker-url`: URL for boot2docker image.
|
||||
- `--vmwarevsphere-vcenter`: IP/hostname for vCenter (or ESXi if connecting directly to a single host).
|
||||
- `--vmwarevsphere-vcenter-port`: vSphere Port for vCenter.
|
||||
- `--vmwarevsphere-network`: Network where the Docker VM will be attached.
|
||||
- `--vmwarevsphere-datastore`: Datastore for Docker VM.
|
||||
- `--vmwarevsphere-datacenter`: Datacenter for Docker VM (must be set to `ha-datacenter` when connecting to a single host).
|
||||
- `--vmwarevsphere-pool`: Resource pool for Docker VM.
|
||||
- `--vmwarevsphere-hostsystem`: vSphere compute resource where the docker VM will be instantiated (use <cluster>/* or <cluster>/<host> if using a cluster).
|
||||
|
||||
The VMware vSphere driver uses the latest boot2docker image.
|
||||
|
||||
#### Environment variables and default values
|
||||
|
||||
| CLI option | Environment variable | Default |
|
||||
| --------------------------------- | ------------------------- | ------------------------ |
|
||||
| **`--vmwarevsphere-username`** | `VSPHERE_USERNAME` | - |
|
||||
| **`--vmwarevsphere-password`** | `VSPHERE_PASSWORD` | - |
|
||||
| `--vmwarevsphere-cpu-count` | `VSPHERE_CPU_COUNT` | `2` |
|
||||
| `--vmwarevsphere-memory-size` | `VSPHERE_MEMORY_SIZE` | `2048` |
|
||||
| `--vmwarevsphere-boot2docker-url` | `VSPHERE_BOOT2DOCKER_URL` | _Latest boot2docker url_ |
|
||||
| `--vmwarevsphere-vcenter` | `VSPHERE_VCENTER` | - |
|
||||
| `--vmwarevsphere-vcenter-port` | `VSPHERE_VCENTER_PORT` | 443 |
|
||||
| `--vmwarevsphere-disk-size` | `VSPHERE_DISK_SIZE` | `20000` |
|
||||
| `--vmwarevsphere-network` | `VSPHERE_NETWORK` | - |
|
||||
| `--vmwarevsphere-datastore` | `VSPHERE_DATASTORE` | - |
|
||||
| `--vmwarevsphere-datacenter` | `VSPHERE_DATACENTER` | - |
|
||||
| `--vmwarevsphere-pool` | `VSPHERE_POOL` | - |
|
||||
| `--vmwarevsphere-hostsystem` | `VSPHERE_HOSTSYSTEM` | - |
|
||||
|
|
@ -0,0 +1,132 @@
|
|||
<!--[metadata]>
|
||||
+++
|
||||
title = "Provision AWS EC2 Instances"
|
||||
description = "Using Docker Machine to provision hosts on AWS"
|
||||
keywords = ["docker, machine, cloud, aws"]
|
||||
[menu.main]
|
||||
parent="cloud_examples"
|
||||
weight=2
|
||||
+++
|
||||
<![end-metadata]-->
|
||||
|
||||
# Amazon Web Services (AWS) EC2 example
|
||||
|
||||
Follow along with this example to create a Dockerized <a href="https://aws.amazon.com/" target="_blank"> Amazon Web Services (AWS)</a> EC2 instance.
|
||||
|
||||
### Step 1. Sign up for AWS and configure credentials
|
||||
|
||||
1. If you are not already an AWS user, sign up for <a href="https://aws.amazon.com/" target="_blank"> AWS</a> to create an account and get root access to EC2 cloud computers.
|
||||
|
||||
If you have an Amazon account, you can use it as your root user account.
|
||||
|
||||
2. Create an IAM (Identity and Access Management) administrator user, an admin group, and a key pair associated with a region.
|
||||
|
||||
From the AWS menus, select **Services** > **IAM** to get started.
|
||||
|
||||
To create machines on AWS, you must supply two parameters:
|
||||
|
||||
* an AWS Access Key ID
|
||||
|
||||
* an AWS Secret Access Key
|
||||
|
||||
See the AWS documentation on <a href="http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/get-set-up-for-amazon-ec2.html" target="_blank">Setting Up with Amazon EC2</a>. Follow the steps for "Create an IAM User" and "Create a Key Pair".
|
||||
|
||||
### Step 2. Use Machine to create the instance
|
||||
|
||||
1. Optionally, create an AWS credential file.
|
||||
|
||||
You can create an `~/.aws/credentials` file to hold your AWS keys so that you don't have to type them every time you run the `docker-machine create` command. Here is an example of a credentials file.
|
||||
|
||||
[default]
|
||||
aws_access_key_id = AKID1234567890
|
||||
aws_secret_access_key = MY-SECRET-KEY
|
||||
|
||||
2. Run `docker-machine create` with the `amazonec2` driver, your keys, and a name for the new instance.
|
||||
|
||||
**Using a credentials file**
|
||||
|
||||
If you specified your keys in a credentials file, this command looks like this to create an instance called `aws-sandbox`:
|
||||
|
||||
docker-machine create --driver amazonec2 aws-sandbox
|
||||
|
||||
**Specifying keys at the command line**
|
||||
|
||||
If you don't have a credentials file, you can use the flags `--amazonec2-access-key` and `--amazonec2-secret-key` on the command line:
|
||||
|
||||
$ docker-machine create --driver amazonec2 --amazonec2-access-key AKI******* --amazonec2-secret-key 8T93C******* aws-sandbox
|
||||
|
||||
**Specifying a region**
|
||||
|
||||
By default, the driver creates new instances in region us-east-1 (North Virginia). You can specify a different region by using the `--amazonec2-region` flag. For example, this command creates a machine called "aws-01" in us-west-1 (Northern California).
|
||||
|
||||
$ docker-machine create --driver amazonec2 --amazonec2-region us-west-1 aws-01
|
||||
|
||||
3. Go to the AWS EC2 Dashboard to view the new instance.
|
||||
|
||||
Log into AWS with your IAM credentials, and navigate to your EC2 Running Instances.
|
||||
|
||||

|
||||
|
||||
**Note**: Make sure you set the region appropriately from the menu in the upper right; otherwise, you won't see the new instance. If you did not specify a region as part of `docker-machine create` (with the optional `--amazonec2-region` flag), then the region will be US East, which is the default.
|
||||
|
||||
3. At the command terminal, run `docker-machine ls`.
|
||||
|
||||
$ docker-machine ls
|
||||
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
|
||||
aws-sandbox * amazonec2 Running tcp://52.90.113.128:2376 v1.10.0
|
||||
default - virtualbox Running tcp://192.168.99.100:2376 v1.10.0-rc4
|
||||
aws-sandbox - digitalocean Running tcp://104.131.43.236:2376 v1.9.1
|
||||
|
||||
The new `aws-sandbox` instance is running, and it is the active host as indicated by the asterisk (*). When you create a new machine, your command shell automatically connects to it. If for some reason your new machine is not the active host, you'll need to run `docker-machine env aws-sandbox`, followed by `eval $(docker-machine env aws-sandbox)` to connect to it.
|
||||
|
||||
### Step 3. Run Docker commands on the instance
|
||||
|
||||
1. Run some `docker-machine` commands to inspect the remote host. For example, `docker-machine ip <machine>` gets the host IP address and `docker-machine inspect <machine>` lists all the details.
|
||||
|
||||
$ docker-machine ip
|
||||
192.168.99.100
|
||||
|
||||
$ docker-machine inspect aws-sandbox
|
||||
{
|
||||
"ConfigVersion": 3,
|
||||
"Driver": {
|
||||
"IPAddress": "52.90.113.128",
|
||||
"MachineName": "aws-sandbox",
|
||||
"SSHUser": "ubuntu",
|
||||
"SSHPort": 22,
|
||||
...
|
||||
|
||||
2. Verify Docker Engine is installed correctly by running `docker` commands.
|
||||
|
||||
Start with something basic like `docker run hello-world`, or for a more interesting test, run a Dockerized webserver on your new remote machine.
|
||||
|
||||
In this example, the `-p` option is used to expose port 80 from the `nginx` container and make it accessible on port `8000` of the `aws-sandbox` host.
|
||||
|
||||
$ docker run -d -p 8000:80 --name webserver kitematic/hello-world-nginx
|
||||
Unable to find image 'kitematic/hello-world-nginx:latest' locally
|
||||
latest: Pulling from kitematic/hello-world-nginx
|
||||
a285d7f063ea: Pull complete
|
||||
2d7baf27389b: Pull complete
|
||||
...
|
||||
Digest: sha256:ec0ca6dcb034916784c988b4f2432716e2e92b995ac606e080c7a54b52b87066
|
||||
Status: Downloaded newer image for kitematic/hello-world-nginx:latest
|
||||
942dfb4a0eaae75bf26c9785ade4ff47ceb2ec2a152be82b9d7960e8b5777e65
|
||||
|
||||
In a web browser, go to `http://<host_ip>:8000` to bring up the webserver home page. You got the `<host_ip>` from the output of the `docker-machine ip <machine>` command you ran in a previous step. Use the port you exposed in the `docker run` command.
|
||||
|
||||

|
||||
|
||||
### Step 4. Use Machine to remove the instance
|
||||
|
||||
To remove an instance and all of its containers and images, first stop the machine, then use `docker-machine rm`:
|
||||
|
||||
$ docker-machine stop aws-sandbox
|
||||
$ docker-machine rm aws-sandbox
|
||||
Do you really want to remove "aws-sandbox"? (y/n): y
|
||||
Successfully removed aws-sandbox
|
||||
## Where to go next
|
||||
|
||||
- [Understand Machine concepts](../concepts.md)
|
||||
- [Docker Machine driver reference](../drivers/index.md)
|
||||
- [Docker Machine subcommand reference](../reference/index.md)
|
||||
- [Provision a Docker Swarm cluster with Docker Machine](/swarm/provision-with-machine.md)
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<!--[metadata]>
|
||||
+++
|
||||
title = "Learn by example"
|
||||
description = "Examples of cloud installs"
|
||||
keywords = ["docker, machine, amazonec2, azure, digitalocean, google, openstack, rackspace, softlayer, virtualbox, vmwarefusion, vmwarevcloudair, vmwarevsphere, exoscale"]
|
||||
[menu.main]
|
||||
parent="workw_machine"
|
||||
identifier="cloud_examples"
|
||||
weight="-50"
|
||||
+++
|
||||
<![end-metadata]-->
|
||||
|
||||
|
||||
# Learn by example
|
||||
|
||||
- [Digital Ocean Example](ocean.md)
|
||||
- [AWS Example](aws.md)
|
||||
|
|
@ -0,0 +1,143 @@
|
|||
<!--[metadata]>
|
||||
+++
|
||||
title = "Provision Digital Ocean Droplets"
|
||||
description = "Using Docker Machine to provision hosts on Digital Ocean"
|
||||
keywords = ["docker, machine, cloud, digital ocean"]
|
||||
[menu.main]
|
||||
parent="cloud_examples"
|
||||
weight=1
|
||||
+++
|
||||
<![end-metadata]-->
|
||||
|
||||
# Digital Ocean example
|
||||
|
||||
Follow along with this example to create a Dockerized <a href="https://digitalocean.com" target="_blank">Digital Ocean</a> Droplet (cloud host).
|
||||
|
||||
### Step 1. Create a Digital Ocean account
|
||||
|
||||
If you have not done so already, go to <a href="https://digitalocean.com" target="_blank">Digital Ocean</a>, create an account, and log in.
|
||||
|
||||
### Step 2. Generate a personal access token
|
||||
|
||||
To generate your access token:
|
||||
|
||||
1. Go to the Digital Ocean administrator console and click **API** in the header.
|
||||
|
||||

|
||||
|
||||
2. Click **Generate New Token** to get to the token generator.
|
||||
|
||||

|
||||
|
||||
3. Give the token a clever name (e.g. "machine"), make sure the **Write (Optional)** checkbox is checked, and click **Generate Token**.
|
||||
|
||||

|
||||
|
||||
4. Grab (copy to clipboard) the generated big long hex string and store it somewhere safe.
|
||||
|
||||

|
||||
|
||||
This is the personal access token you'll use in the next step to create your cloud server.
|
||||
|
||||
### Step 3. Use Machine to create the Droplet
|
||||
|
||||
1. Run `docker-machine create` with the `digitalocean` driver and pass your key to the `--digitalocean-access-token` flag, along with a name for the new cloud server.
|
||||
|
||||
For this example, we'll call our new Droplet "docker-sandbox".
|
||||
|
||||
$ docker-machine create --driver digitalocean --digitalocean-access-token xxxxx docker-sandbox
|
||||
Running pre-create checks...
|
||||
Creating machine...
|
||||
(docker-sandbox) OUT | Creating SSH key...
|
||||
(docker-sandbox) OUT | Creating Digital Ocean droplet...
|
||||
(docker-sandbox) OUT | Waiting for IP address to be assigned to the Droplet...
|
||||
Waiting for machine to be running, this may take a few minutes...
|
||||
Machine is running, waiting for SSH to be available...
|
||||
Detecting operating system of created instance...
|
||||
Detecting the provisioner...
|
||||
Provisioning created instance...
|
||||
Copying certs to the local machine directory...
|
||||
Copying certs to the remote machine...
|
||||
Setting Docker configuration on the remote daemon...
|
||||
To see how to connect Docker to this machine, run: docker-machine env docker-sandbox
|
||||
|
||||
When the Droplet is created, Docker generates a unique SSH key and stores it on your local system in `~/.docker/machines`. Initially, this is used to provision the host. Later, it's used under the hood to access the Droplet directly with the `docker-machine ssh` command. Docker Engine is installed on the cloud server and the daemon is configured to accept remote connections over TCP using TLS for authentication.
|
||||
|
||||
2. Go to the Digital Ocean console to view the new Droplet.
|
||||
|
||||

|
||||
|
||||
3. At the command terminal, run `docker-machine ls`.
|
||||
|
||||
$ docker-machine ls
|
||||
NAME ACTIVE DRIVER STATE URL SWARM
|
||||
default - virtualbox Running tcp://192.168.99.100:2376
|
||||
docker-sandbox * digitalocean Running tcp://45.55.139.48:2376
|
||||
|
||||
The new `docker-sandbox` machine is running, and it is the active host as indicated by the asterisk (*). When you create a new machine, your command shell automatically connects to it. If for some reason your new machine is not the active host, you'll need to run `docker-machine env docker-sandbox`, followed by `eval $(docker-machine env docker-sandbox)` to connect to it.
|
||||
|
||||
### Step 4. Run Docker commands on the Droplet
|
||||
|
||||
1. Run some `docker-machine` commands to inspect the remote host. For example, `docker-machine ip <machine>` gets the host IP adddress and `docker-machine inspect <machine>` lists all the details.
|
||||
|
||||
$ docker-machine ip docker-sandbox
|
||||
104.131.43.236
|
||||
|
||||
$ docker-machine inspect docker-sandbox
|
||||
{
|
||||
"ConfigVersion": 3,
|
||||
"Driver": {
|
||||
"IPAddress": "104.131.43.236",
|
||||
"MachineName": "docker-sandbox",
|
||||
"SSHUser": "root",
|
||||
"SSHPort": 22,
|
||||
"SSHKeyPath": "/Users/samanthastevens/.docker/machine/machines/docker-sandbox/id_rsa",
|
||||
"StorePath": "/Users/samanthastevens/.docker/machine",
|
||||
"SwarmMaster": false,
|
||||
"SwarmHost": "tcp://0.0.0.0:3376",
|
||||
"SwarmDiscovery": "",
|
||||
...
|
||||
|
||||
2. Verify Docker Engine is installed correctly by running `docker` commands.
|
||||
|
||||
Start with something basic like `docker run hello-world`, or for a more interesting test, run a Dockerized webserver on your new remote machine.
|
||||
|
||||
In this example, the `-p` option is used to expose port 80 from the `nginx` container and make it accessible on port `8000` of the `docker-sandbox` host.
|
||||
|
||||
$ docker run -d -p 8000:80 --name webserver kitematic/hello-world-nginx
|
||||
Unable to find image 'kitematic/hello-world-nginx:latest' locally
|
||||
latest: Pulling from kitematic/hello-world-nginx
|
||||
a285d7f063ea: Pull complete
|
||||
2d7baf27389b: Pull complete
|
||||
...
|
||||
Digest: sha256:ec0ca6dcb034916784c988b4f2432716e2e92b995ac606e080c7a54b52b87066
|
||||
Status: Downloaded newer image for kitematic/hello-world-nginx:latest
|
||||
942dfb4a0eaae75bf26c9785ade4ff47ceb2ec2a152be82b9d7960e8b5777e65
|
||||
|
||||
In a web browser, go to `http://<host_ip>:8000` to bring up the webserver home page. You got the `<host_ip>` from the output of the `docker-machine ip <machine>` command you ran in a previous step. Use the port you exposed in the `docker run` command.
|
||||
|
||||

|
||||
|
||||
### Step 5. Use Machine to remove the Droplet
|
||||
|
||||
To remove a host and all of its containers and images, first stop the machine, then use `docker-machine rm`:
|
||||
|
||||
$ docker-machine stop docker-sandbox
|
||||
$ docker-machine rm docker-sandbox
|
||||
Do you really want to remove "docker-sandbox"? (y/n): y
|
||||
Successfully removed docker-sandbox
|
||||
|
||||
$ docker-machine ls
|
||||
NAME ACTIVE DRIVER STATE URL SWARM
|
||||
default * virtualbox Running tcp:////xxx.xxx.xx.xxx:xxxx
|
||||
|
||||
If you monitor the Digital Ocean console while you run these commands, you will see it update first to reflect that the Droplet was stopped, and then removed.
|
||||
|
||||
If you create a host with Docker Machine, but remove it through the cloud provider console, Machine will lose track of the server status. So please use the `docker-machine rm` command for hosts you create with `docker-machine create`.
|
||||
|
||||
## Where to go next
|
||||
|
||||
- [Understand Machine concepts](../concepts.md)
|
||||
- [Docker Machine driver reference](../drivers/index.md)
|
||||
- [Docker Machine subcommand reference](../reference/index.md)
|
||||
- [Provision a Docker Swarm cluster with Docker Machine](/swarm/provision-with-machine.md)
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
<!--[metadata]>
|
||||
+++
|
||||
title = "Provision hosts in the cloud"
|
||||
description = "Using Docker Machine to provision hosts on cloud providers"
|
||||
keywords = ["docker, machine, amazonec2, azure, digitalocean, google, openstack, rackspace, softlayer, virtualbox, vmwarefusion, vmwarevcloudair, vmwarevsphere, exoscale"]
|
||||
[menu.main]
|
||||
parent="workw_machine"
|
||||
weight=-60
|
||||
+++
|
||||
<![end-metadata]-->
|
||||
|
||||
# Use Docker Machine to provision hosts on cloud providers
|
||||
|
||||
Docker Machine driver plugins are available for many cloud platforms, so you can use Machine to provision cloud hosts. When you use Docker Machine for provisioning, you create cloud hosts with Docker Engine installed on them.
|
||||
|
||||
You'll need to install and run Docker Machine, and create an account with the cloud provider.
|
||||
|
||||
Then you provide account verification, security credentials, and configuration options for the providers as flags to `docker-machine create`. The flags are unique for each cloud-specific driver. For instance, to pass a Digital Ocean access token you use the `--digitalocean-access-token` flag. Take a look at the examples below for Digital Ocean and AWS.
|
||||
|
||||
## Examples
|
||||
|
||||
### Digital Ocean
|
||||
|
||||
For Digital Ocean, this command creates a Droplet (cloud host) called "docker-sandbox".
|
||||
|
||||
$ docker-machine create --driver digitalocean --digitalocean-access-token xxxxx docker-sandbox
|
||||
|
||||
For a step-by-step guide on using Machine to create Docker hosts on Digital Ocean, see the [Digital Ocean Example](examples/ocean.md).
|
||||
|
||||
### Amazon Web Services (AWS)
|
||||
|
||||
For AWS EC2, this command creates an instance called "aws-sandbox":
|
||||
|
||||
$ docker-machine create --driver amazonec2 --amazonec2-access-key AKI******* --amazonec2-secret-key 8T93C******* aws-sandbox
|
||||
|
||||
For a step-by-step guide on using Machine to create Dockerized AWS instances, see the [Amazon Web Services (AWS) example](examples/aws.md).
|
||||
|
||||
## The docker-machine create command
|
||||
|
||||
The `docker-machine create` command typically requires that you specify, at a minimum:
|
||||
|
||||
* `--driver` - to indicate the provider on which to create the machine (VirtualBox, DigitalOcean, AWS, and so on)
|
||||
|
||||
* Account verification and security credentials (for cloud providers), specific to the cloud service you are using
|
||||
|
||||
* `<machine>` - name of the host you want to create
|
||||
|
||||
For convenience, `docker-machine` will use sensible defaults for choosing settings such as the image that the server is based on, but you override the defaults using the respective flags (e.g. `--digitalocean-image`). This is useful if, for example, you want to create a cloud server with a lot of memory and CPUs (by default `docker-machine` creates a small server).
|
||||
|
||||
For a full list of the flags/settings available and their defaults, see the output of `docker-machine create -h` at the command line, the <a href="../reference/create/" target="_blank">create</a> command in the Machine <a href="../reference/" target="_blank">command line reference</a>, and <a href="https://docs.docker.com/machine/drivers/os-base/" target="_blank">driver options and operating system defaults</a> in the Machine driver reference.
|
||||
|
||||
## Drivers for cloud providers
|
||||
|
||||
When you install Docker Machine, you get a set of drivers for various cloud providers (like Amazon Web Services, Digital Ocean, or Microsoft Azure) and local providers (like Oracle VirtualBox, VMWare Fusion, or Microsoft Hyper-V).
|
||||
|
||||
See <a href="../drivers/" target="_blank">Docker Machine driver reference</a> for details on the drivers, including required flags and configuration options (which vary by provider).
|
||||
|
||||
## 3rd-party driver plugins
|
||||
|
||||
Several Docker Machine driver plugins for use with other cloud platforms are available from 3rd party contributors. These are use-at-your-own-risk plugins, not maintained by or formally associated with Docker.
|
||||
|
||||
See <a href="https://github.com/docker/machine/blob/master/docs/AVAILABLE_DRIVER_PLUGINS.md" target="_blank">Available driver plugins</a> in the docker/machine repo on GitHub.
|
||||
|
||||
## Adding a host without a driver
|
||||
|
||||
You can add a host to Docker which only has a URL and no driver. Then you can use the machine name you provide here for an existing host so you don’t have to type out the URL every time you run a Docker command.
|
||||
|
||||
$ docker-machine create --url=tcp://50.134.234.20:2376 custombox
|
||||
$ docker-machine ls
|
||||
NAME ACTIVE DRIVER STATE URL
|
||||
custombox * none Running tcp://50.134.234.20:2376
|
||||
|
||||
## Using Machine to provision Docker Swarm clusters
|
||||
|
||||
Docker Machine can also provision <a href="https://docs.docker.com/swarm/overview/" target="_blank">Docker Swarm</a> clusters. This can be used with any driver and will be secured with TLS.
|
||||
|
||||
* To get started with Swarm, see <a href="https://docs.docker.com/swarm/get-swarm/" target="_blank">How to get Docker Swarm</a>.
|
||||
|
||||
* To learn how to use Machine to provision a Swarm cluster, see <a href="https://docs.docker.com/swarm/provision-with-machine/" target="_blank">Provision a Swarm cluster with Docker Machine</a>.
|
||||
|
||||
## Where to go next
|
||||
- Example: Provision Dockerized [Digital Ocean Droplets](examples/ocean.md)
|
||||
- Example: Provision Dockerized [AWS EC2 Instances](examples/aws.md)
|
||||
- [Understand Machine concepts](concepts.md)
|
||||
- [Docker Machine driver reference](drivers/index.md)
|
||||
- [Docker Machine subcommand reference](reference/index.md)
|
||||
- [Provision a Docker Swarm cluster with Docker Machine](/swarm/provision-with-machine.md)
|
||||
|
|
@ -0,0 +1,331 @@
|
|||
<!--[metadata]>
|
||||
+++
|
||||
title = "Get started with a local VM"
|
||||
description = "Get started with Docker Machine and a local VM"
|
||||
keywords = ["docker, machine, virtualbox, local"]
|
||||
[menu.main]
|
||||
parent="workw_machine"
|
||||
weight=-70
|
||||
+++
|
||||
<![end-metadata]-->
|
||||
|
||||
# Get started with Docker Machine and a local VM
|
||||
|
||||
Let's take a look at using `docker-machine` to create, use and manage a
|
||||
Docker host inside of a local virtual machine.
|
||||
|
||||
## Prerequisite Information
|
||||
|
||||
With the advent of [Docker for Mac](/docker-for-mac/index.md) and [Docker for
|
||||
Windows](/docker-for-windows/index.md) as replacements for [Docker
|
||||
Toolbox](/toolbox/overview.md), we recommend that you use these for your primary
|
||||
Docker workflows.
|
||||
|
||||
However, Docker Machine is still available to create and manage machines for
|
||||
power users or multi-node experimentation. Both Docker for Mac and Docker for
|
||||
Windows include the newest version of Docker Machine, so when you install either
|
||||
of these, you get `docker-machine`.
|
||||
|
||||
The new solutions come with their own native virtualization solutions rather
|
||||
than using Oracle VirtualBox, so there are new considerations to keep in mind
|
||||
when using Machine to create local VMs. Docker for Mac allows for the creation
|
||||
of additional `docker-machine` based `virtualbox` machines without issue, but in
|
||||
the case of Docker for Windows, you will need to use the `hyperv` driver
|
||||
instead.
|
||||
|
||||
|
||||
#### If you are using Docker for Windows
|
||||
|
||||
Docker for Windows uses [Microsoft
|
||||
Hyper-V](https://msdn.microsoft.com/en-us/virtualization/hyperv_on_windows/windows_welcome)
|
||||
for virtualization, and Hyper-V and Oracle VirtualBox are incompatible.
|
||||
Therefore, you cannot run the two solutions simultaneously. But you can still
|
||||
use `docker-machine` to create local VMs by using the Microsoft Hyper-V driver.
|
||||
|
||||
* If you are using Docker for Windows, the only prequisite is to have Docker for Windows installed. You will use the Microsoft `hyperv` driver to create
|
||||
local machines. (See the [Docker Machine driver for Microsoft
|
||||
Hyper-V](drivers/hyper-v.md).)
|
||||
|
||||
#### If you are using Docker for Mac
|
||||
|
||||
Docker for Mac uses [HyperKit](https://github.com/docker/HyperKit/), a
|
||||
lightweight OS X virtualization solution built on top of the
|
||||
[Hypervisor.framework](https://developer.apple.com/reference/hypervisor) in OS X
|
||||
10.10 Yosemite and higher.
|
||||
|
||||
Currently, there is no `docker-machine create` driver for HyperKit, so you will
|
||||
use `virtualbox` driver to create local machines. (See the [Docker Machine
|
||||
driver for Oracle VirtualBox](drivers/virtualbox.md).) Note that you can run
|
||||
both HyperKit and Oracle VirtualBox on the same system. To learn more, see
|
||||
[Docker for Mac vs. Docker Toolbox](/docker-for-mac/docker-toolbox/).
|
||||
|
||||
* Make sure you have <a href="https://www.virtualbox.org/wiki/Downloads" target="_blank">the latest VirtualBox</a> correctly installed on your system
|
||||
(either as part of an earlier Toolbox install, or manual install).
|
||||
|
||||
#### If you are using Docker Toolbox
|
||||
|
||||
Docker for Mac and Docker for Windows both require newer versions of their
|
||||
respective operating systems, so users with older OS versions must use Docker
|
||||
Toolbox.
|
||||
|
||||
* If you are using Docker Toolbox on either Mac or an older version Windows system (without Hyper-V), you will use the `virtualbox` driver to create
|
||||
a local machine based on Oracle <a href= "https://www.virtualbox.org/"
|
||||
target="_blank">VirtualBox</a>. (See the [Docker Machine driver for Oracle
|
||||
VirtualBox](drivers/virtualbox.md) )
|
||||
<br />
|
||||
* If you are using Docker Toolbox on a Windows system that has Hyper-V but cannot run Docker for Windows (for example Windows 8 Pro), you must use the
|
||||
`hyperv` driver to create local machines. (See the [Docker Machine driver for
|
||||
Microsoft Hyper-V](drivers/hyper-v.md).)
|
||||
<br />
|
||||
* Make sure you have <a href="https://www.virtualbox.org/wiki/Downloads" target="_blank">the latest VirtualBox</a> correctly installed on your system. If
|
||||
you used <a href="https://www.docker.com/products/docker-toolbox"
|
||||
target="_blank">Toolbox</a> for <a
|
||||
href="https://docs.docker.com/engine/installation/mac/" target="_blank">Mac</a>
|
||||
or <a href="https://docs.docker.com/engine/installation/windows/"
|
||||
target="_blank">Windows</a> to install Docker Machine, VirtualBox is
|
||||
automatically installed.
|
||||
<br />
|
||||
* If you used the Quickstart Terminal to launch your first machine and set your terminal environment to point to it, a default machine was automatically
|
||||
created. If this is the case, you can still follow along with these steps, but
|
||||
create another machine and name it something other than "default" (e.g., staging
|
||||
or sandbox).
|
||||
|
||||
## Use Machine to run Docker containers
|
||||
|
||||
To run a Docker container, you:
|
||||
|
||||
* create a new (or start an existing) Docker virtual machine
|
||||
* switch your environment to your new VM
|
||||
* use the docker client to create, load, and manage containers
|
||||
|
||||
Once you create a machine, you can reuse it as often as you like. Like any VirtualBox VM, it maintains its configuration between uses.
|
||||
|
||||
The examples here show how to create and start a machine, run Docker commands, and work with containers.
|
||||
|
||||
## Create a machine
|
||||
|
||||
1. Open a command shell or terminal window.
|
||||
|
||||
These command examples shows a Bash shell. For a different shell, such as C Shell, the same commands are the same except where noted.
|
||||
|
||||
2. Use `docker-machine ls` to list available machines.
|
||||
|
||||
In this example, no machines have been created yet.
|
||||
|
||||
$ docker-machine ls
|
||||
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
|
||||
|
||||
3. Create a machine.
|
||||
|
||||
Run the `docker-machine create` command, pass the appropriate driver to the
|
||||
`--driver` flag and provide a machine name. If this is your first machine, name
|
||||
it `default` as shown in the example. If you already have a "default" machine,
|
||||
choose another name for this new machine.
|
||||
|
||||
* If you are using Toolbox on Mac, Toolbox on older Windows systems without Hyper-V, or Docker for Mac, use `virtualbox` as the driver, as shown in this example. (The Docker Machine VirtualBox driver reference is [here](drivers/virtualbox.md).) (See [prerequisites](#prerequisite-information) above to learn more.)
|
||||
|
||||
* On Docker for Windows systems that support Hyper-V, use the `hyperv` driver as shown in the [Docker Machine Microsoft Hyper-V driver reference](drivers/hyper-v.md). (See [prerequisites](#prerequisite-information) above to learn more.)
|
||||
|
||||
$ docker-machine create --driver virtualbox default
|
||||
Running pre-create checks...
|
||||
Creating machine...
|
||||
(staging) Copying /Users/ripley/.docker/machine/cache/boot2docker.iso to /Users/ripley/.docker/machine/machines/default/boot2docker.iso...
|
||||
(staging) Creating VirtualBox VM...
|
||||
(staging) Creating SSH key...
|
||||
(staging) Starting the VM...
|
||||
(staging) Waiting for an IP...
|
||||
Waiting for machine to be running, this may take a few minutes...
|
||||
Machine is running, waiting for SSH to be available...
|
||||
Detecting operating system of created instance...
|
||||
Detecting the provisioner...
|
||||
Provisioning with boot2docker...
|
||||
Copying certs to the local machine directory...
|
||||
Copying certs to the remote machine...
|
||||
Setting Docker configuration on the remote daemon...
|
||||
Checking connection to Docker...
|
||||
Docker is up and running!
|
||||
To see how to connect Docker to this machine, run: docker-machine env default
|
||||
|
||||
This command downloads a lightweight Linux distribution (<a href="https://github.com/boot2docker/boot2docker" target="_blank">boot2docker</a>) with the Docker daemon installed, and creates and starts a VirtualBox VM with Docker running.
|
||||
|
||||
4. List available machines again to see your newly minted machine.
|
||||
|
||||
$ docker-machine ls
|
||||
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
|
||||
default * virtualbox Running tcp://192.168.99.187:2376 v1.9.1
|
||||
|
||||
5. Get the environment commands for your new VM.
|
||||
|
||||
As noted in the output of the `docker-machine create` command, you need to tell Docker to talk to the new machine. You can do this with the `docker-machine env` command.
|
||||
|
||||
$ docker-machine env default
|
||||
export DOCKER_TLS_VERIFY="1"
|
||||
export DOCKER_HOST="tcp://172.16.62.130:2376"
|
||||
export DOCKER_CERT_PATH="/Users/<yourusername>/.docker/machine/machines/default"
|
||||
export DOCKER_MACHINE_NAME="default"
|
||||
# Run this command to configure your shell:
|
||||
# eval "$(docker-machine env default)"
|
||||
|
||||
6. Connect your shell to the new machine.
|
||||
|
||||
$ eval "$(docker-machine env default)"
|
||||
|
||||
**Note**: If you are using `fish`, or a Windows shell such as
|
||||
Powershell/`cmd.exe` the above method will not work as described.
|
||||
Instead, see <a href="https://docs.docker.com/machine/reference/env/" target="_blank">the `env` command's documentation</a>
|
||||
to learn how to set the environment variables for your shell.
|
||||
|
||||
This sets environment variables for the current shell that the Docker client will read which specify the TLS settings. You need to do this each time you open a new shell or restart your machine.
|
||||
|
||||
You can now run Docker commands on this host.
|
||||
|
||||
## Run containers and experiment with Machine commands
|
||||
|
||||
Run a container with `docker run` to verify your set up.
|
||||
|
||||
1. Use `docker run` to download and run `busybox` with a simple 'echo' command.
|
||||
|
||||
$ docker run busybox echo hello world
|
||||
Unable to find image 'busybox' locally
|
||||
Pulling repository busybox
|
||||
e72ac664f4f0: Download complete
|
||||
511136ea3c5a: Download complete
|
||||
df7546f9f060: Download complete
|
||||
e433a6c5b276: Download complete
|
||||
hello world
|
||||
|
||||
2. Get the host IP address.
|
||||
|
||||
Any exposed ports are available on the Docker host’s IP address, which you can get using the `docker-machine ip` command:
|
||||
|
||||
$ docker-machine ip default
|
||||
192.168.99.100
|
||||
|
||||
3. Run a webserver (<a href="https://www.nginx.com/" target="_blank">nginx</a>) in a container with the following command:
|
||||
|
||||
$ docker run -d -p 8000:80 nginx
|
||||
|
||||
When the image is finished pulling, you can hit the server at port 8000 on the IP address given to you by `docker-machine ip`. For instance:
|
||||
|
||||
$ curl $(docker-machine ip default):8000
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Welcome to nginx!</title>
|
||||
<style>
|
||||
body {
|
||||
width: 35em;
|
||||
margin: 0 auto;
|
||||
font-family: Tahoma, Verdana, Arial, sans-serif;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Welcome to nginx!</h1>
|
||||
<p>If you see this page, the nginx web server is successfully installed and
|
||||
working. Further configuration is required.</p>
|
||||
|
||||
<p>For online documentation and support please refer to
|
||||
<a href="http://nginx.org/">nginx.org</a>.<br/>
|
||||
Commercial support is available at
|
||||
<a href="http://nginx.com/">nginx.com</a>.</p>
|
||||
|
||||
<p><em>Thank you for using nginx.</em></p>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
You can create and manage as many local VMs running Docker as you please; just run `docker-machine create` again. All created machines will appear in the output of `docker-machine ls`.
|
||||
|
||||
## Start and stop machines
|
||||
|
||||
If you are finished using a host for the time being, you can stop it with `docker-machine stop` and later start it again with `docker-machine start`.
|
||||
|
||||
$ docker-machine stop default
|
||||
$ docker-machine start default
|
||||
|
||||
## Operate on machines without specifying the name
|
||||
|
||||
Some `docker-machine` commands will assume that the given operation should be run on a machine named `default` (if it exists) if no machine name is specified. Because using a local VM named `default` is such a common pattern, this allows you to save some typing on the most frequently used Machine commands.
|
||||
|
||||
For example:
|
||||
|
||||
$ docker-machine stop
|
||||
Stopping "default"....
|
||||
Machine "default" was stopped.
|
||||
|
||||
$ docker-machine start
|
||||
Starting "default"...
|
||||
(default) Waiting for an IP...
|
||||
Machine "default" was started.
|
||||
Started machines may have new IP addresses. You may need to re-run the `docker-machine env` command.
|
||||
|
||||
$ eval $(docker-machine env)
|
||||
|
||||
$ docker-machine ip
|
||||
192.168.99.100
|
||||
|
||||
Commands that follow this style are:
|
||||
|
||||
- `docker-machine config`
|
||||
- `docker-machine env`
|
||||
- `docker-machine inspect`
|
||||
- `docker-machine ip`
|
||||
- `docker-machine kill`
|
||||
- `docker-machine provision`
|
||||
- `docker-machine regenerate-certs`
|
||||
- `docker-machine restart`
|
||||
- `docker-machine ssh`
|
||||
- `docker-machine start`
|
||||
- `docker-machine status`
|
||||
- `docker-machine stop`
|
||||
- `docker-machine upgrade`
|
||||
- `docker-machine url`
|
||||
|
||||
For machines other than `default`, and commands other than those listed above, you must always specify the name explicitly as an argument.
|
||||
|
||||
## Start local machines on startup
|
||||
|
||||
In order to ensure that the Docker client is automatically configured at the
|
||||
start of each shell session, some users like to embed `eval $(docker-machine env
|
||||
default)` in their shell profiles (e.g., the `~/.bash_profile` file). However,
|
||||
this fails if the `default` machine is not running. If desired, you can
|
||||
configure your system to start the `default` machine automatically.
|
||||
|
||||
Here is an example of how to configure this on OS X.
|
||||
|
||||
Create a file called `com.docker.machine.default.plist` under `~/Library/LaunchAgents` with the following content:
|
||||
|
||||
```
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>EnvironmentVariables</key>
|
||||
<dict>
|
||||
<key>PATH</key>
|
||||
<string>/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin</string>
|
||||
</dict>
|
||||
<key>Label</key>
|
||||
<string>com.docker.machine.default</string>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>/usr/local/bin/docker-machine</string>
|
||||
<string>start</string>
|
||||
<string>default</string>
|
||||
</array>
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
```
|
||||
|
||||
You can change the `default` string above to make this `LaunchAgent` start any machine(s) you desire.
|
||||
|
||||
## Where to go next
|
||||
|
||||
- Provision multiple Docker hosts [on your cloud provider](get-started-cloud.md)
|
||||
- [Understand Machine concepts](concepts.md)
|
||||
- [Docker Machine list of reference pages for all supported drivers](/machine/drivers/index.md)
|
||||
- [Docker Machine driver for Oracle VirtualBox](drivers/virtualbox.md)
|
||||
- [Docker Machine driver for Microsoft Hyper-V](drivers/hyper-v.md)
|
||||
- [`docker-machine` command line reference](reference/index.md)
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 348 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 311 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 194 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue