Compare commits
115 Commits
v0.6.0-rc5
...
v0.7.0-rc5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
200052dd56 | ||
|
|
0107f361bb | ||
|
|
ea86e4e087 | ||
|
|
ddbedb9b31 | ||
|
|
94fd38c358 | ||
|
|
fb6442f9bb | ||
|
|
e8ffaedb08 | ||
|
|
d4fbc039a7 | ||
|
|
9654e4eaad | ||
|
|
2217e88611 | ||
|
|
4dd735bad4 | ||
|
|
4eaf18946a | ||
|
|
b6c103373e | ||
|
|
edd9d33eaa | ||
|
|
3e3e036bda | ||
|
|
f5681217b5 | ||
|
|
01970f5651 | ||
|
|
76b360706f | ||
|
|
dfc9455e72 | ||
|
|
c681c71fa6 | ||
|
|
805e865bfb | ||
|
|
e89f4f27e0 | ||
|
|
8b9edc2836 | ||
|
|
897cbb9e9f | ||
|
|
e97ad03b99 | ||
|
|
6ace0c1b05 | ||
|
|
a05e41ca6b | ||
|
|
bb824a9f1b | ||
|
|
2f2be31d8d | ||
|
|
0a3ee71352 | ||
|
|
314e1a69ae | ||
|
|
375e38c850 | ||
|
|
cc538ad523 | ||
|
|
cdd3dcc99e | ||
|
|
4b121ec3b1 | ||
|
|
f56501251e | ||
|
|
a811132245 | ||
|
|
2122f99fce | ||
|
|
05a43ceac0 | ||
|
|
bdcda45a11 | ||
|
|
99420925f5 | ||
|
|
2fc1d3fa5f | ||
|
|
7adb6ee3a8 | ||
|
|
395ef0b8c4 | ||
|
|
cf998978a8 | ||
|
|
5987d713a4 | ||
|
|
e5293ff926 | ||
|
|
3bbf46b66d | ||
|
|
1da9a19951 | ||
|
|
b65e429bb6 | ||
|
|
5cc30c6935 | ||
|
|
7ac9d02614 | ||
|
|
e58a6a5433 | ||
|
|
1731fc5642 | ||
|
|
32b2ccda1e | ||
|
|
065fe4a16e | ||
|
|
bdf266e655 | ||
|
|
a23776f03f | ||
|
|
133c2610d1 | ||
|
|
c1582f5a49 | ||
|
|
2781eab500 | ||
|
|
9e14a9da5a | ||
|
|
605a8bf618 | ||
|
|
d28dfe1e19 | ||
|
|
d782d0b17f | ||
|
|
a22075aef2 | ||
|
|
e0110932f4 | ||
|
|
2751101ebe | ||
|
|
1de9d2d6c7 | ||
|
|
8777e477b8 | ||
|
|
b536e6ea35 | ||
|
|
17f0ef63b9 | ||
|
|
2b622f88dc | ||
|
|
71ce9e4aa3 | ||
|
|
e315240651 | ||
|
|
21cf86665b | ||
|
|
c080cadea0 | ||
|
|
238c393640 | ||
|
|
d01440fe55 | ||
|
|
9122378855 | ||
|
|
b551fad495 | ||
|
|
68e6e0ab33 | ||
|
|
bca966a3a9 | ||
|
|
ce523d98f4 | ||
|
|
5ab3f500d6 | ||
|
|
70f2c8dd3a | ||
|
|
3b68017af5 | ||
|
|
2081b1be95 | ||
|
|
73d0790e30 | ||
|
|
3262aa7497 | ||
|
|
17e01a3771 | ||
|
|
4d8da95bc7 | ||
|
|
70df9efc4c | ||
|
|
d4a026dc5d | ||
|
|
5d02e35df7 | ||
|
|
22a3722aa0 | ||
|
|
258a5a173c | ||
|
|
2e6f31599a | ||
|
|
345aa57c80 | ||
|
|
0631e90ee0 | ||
|
|
864990e640 | ||
|
|
39e4bf7e5d | ||
|
|
aa10d84b75 | ||
|
|
bd904fcbda | ||
|
|
b11ed7c1ef | ||
|
|
f5be4686d5 | ||
|
|
ec85b27621 | ||
|
|
c2a58aeb70 | ||
|
|
c54da1924b | ||
|
|
05132c0dd6 | ||
|
|
bffe4e5d3e | ||
|
|
f64314b82f | ||
|
|
92e20827d8 | ||
|
|
22ab564dac | ||
|
|
d725d99fa5 |
14
.drone.yml
14
.drone.yml
@@ -1,6 +1,8 @@
|
||||
build:
|
||||
image: rancher/dapper:1.10.3
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
commands:
|
||||
- dapper ci
|
||||
---
|
||||
pipeline:
|
||||
build:
|
||||
image: rancher/dapper:1.10.3
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
commands:
|
||||
- dapper ci
|
||||
|
||||
@@ -29,13 +29,11 @@ RUN apt-get update && \
|
||||
module-init-tools \
|
||||
openssh-client \
|
||||
pkg-config \
|
||||
python-pip \
|
||||
qemu \
|
||||
qemu-kvm \
|
||||
rsync \
|
||||
sudo \
|
||||
syslinux-common \
|
||||
tox \
|
||||
vim \
|
||||
wget \
|
||||
xorriso
|
||||
@@ -55,7 +53,7 @@ ARG DOCKER_BUILD_VERSION=1.10.3
|
||||
ARG DOCKER_BUILD_PATCH_VERSION=v${DOCKER_BUILD_VERSION}-ros1
|
||||
ARG SELINUX_POLICY_URL=https://github.com/rancher/refpolicy/releases/download/v0.0.3/policy.29
|
||||
|
||||
ARG KERNEL_URL_amd64=https://github.com/rancher/os-kernel/releases/download/Ubuntu-4.4.0-34.53-rancher/linux-4.4.15-rancher-x86.tar.gz
|
||||
ARG KERNEL_URL_amd64=https://github.com/rancher/os-kernel/releases/download/v4.4.24-rancher1/linux-4.4.24-rancher-x86.tar.gz
|
||||
ARG KERNEL_URL_arm64=https://github.com/imikushin/os-kernel/releases/download/Estuary-4.4.0-arm64.8/linux-4.4.0-rancher-arm64.tar.gz
|
||||
|
||||
ARG DOCKER_URL_amd64=https://get.docker.com/builds/Linux/x86_64/docker-${DOCKER_VERSION}.tgz
|
||||
@@ -72,9 +70,9 @@ ARG OS_SERVICES_REPO=https://raw.githubusercontent.com/${OS_REPO}/os-services
|
||||
ARG IMAGE_NAME=${OS_REPO}/os
|
||||
ARG DFS_IMAGE=${OS_REPO}/docker:v${DOCKER_VERSION}-2
|
||||
|
||||
ARG OS_BASE_URL_amd64=https://github.com/rancher/os-base/releases/download/v2016.05-5/os-base_amd64.tar.xz
|
||||
ARG OS_BASE_URL_arm64=https://github.com/rancher/os-base/releases/download/v2016.05-5/os-base_arm64.tar.xz
|
||||
ARG OS_BASE_URL_arm=https://github.com/rancher/os-base/releases/download/v2016.05-5/os-base_arm.tar.xz
|
||||
ARG OS_BASE_URL_amd64=https://github.com/rancher/os-base/releases/download/v2016.08.1-1/os-base_amd64.tar.xz
|
||||
ARG OS_BASE_URL_arm64=https://github.com/rancher/os-base/releases/download/v2016.08.1-1/os-base_arm64.tar.xz
|
||||
ARG OS_BASE_URL_arm=https://github.com/rancher/os-base/releases/download/v2016.08.1-1/os-base_arm.tar.xz
|
||||
######################################################
|
||||
|
||||
# Set up environment and export all ARGS as ENV
|
||||
@@ -96,7 +94,7 @@ ENV BUILD_DOCKER_URL=BUILD_DOCKER_URL_${ARCH} \
|
||||
DOCKER_VERSION=${DOCKER_VERSION} \
|
||||
DOWNLOADS=/usr/src/downloads \
|
||||
GOPATH=/go \
|
||||
GO_VERSION=1.6.2 \
|
||||
GO_VERSION=1.7.1 \
|
||||
GOARCH=$ARCH \
|
||||
HOSTNAME_DEFAULT=${HOSTNAME_DEFAULT} \
|
||||
IMAGE_NAME=${IMAGE_NAME} \
|
||||
|
||||
12
Makefile
12
Makefile
@@ -18,15 +18,15 @@ trash-keep: .dapper
|
||||
|
||||
deps: trash
|
||||
|
||||
build/initrd/.id:
|
||||
dapper prepare
|
||||
build/initrd/.id: .dapper
|
||||
./.dapper prepare
|
||||
|
||||
run: build/initrd/.id
|
||||
dapper -m bind build-target
|
||||
run: build/initrd/.id .dapper
|
||||
./.dapper -m bind build-target
|
||||
./scripts/run
|
||||
|
||||
shell-bind:
|
||||
dapper -m bind -s
|
||||
shell-bind: .dapper
|
||||
./.dapper -m bind -s
|
||||
|
||||
clean:
|
||||
@./scripts/clean
|
||||
|
||||
62
README.md
62
README.md
@@ -14,38 +14,40 @@ it would really be bad if somebody did `docker rm -f $(docker ps -qa)` and delet
|
||||
|
||||
## Latest Release
|
||||
|
||||
**v0.5.0 - Docker 1.11.2 - Linux 4.4.10**
|
||||
**v0.6.1 - Docker 1.12.1 - Linux 4.4.19**
|
||||
|
||||
### ISO
|
||||
|
||||
https://releases.rancher.com/os/latest/rancheros.iso
|
||||
https://releases.rancher.com/os/v0.5.0/rancheros.iso
|
||||
|
||||
**Note**: you must login using `rancher` for username and password.
|
||||
https://releases.rancher.com/os/v0.6.1/rancheros.iso
|
||||
|
||||
### Additional Downloads
|
||||
|
||||
#### Latest
|
||||
#### Latest
|
||||
|
||||
* https://releases.rancher.com/os/latest/initrd
|
||||
* https://releases.rancher.com/os/latest/iso-checksums.txt
|
||||
* https://releases.rancher.com/os/latest/rancheros-openstack.img
|
||||
* https://releases.rancher.com/os/latest/rancheros-raspberry-pi.zip
|
||||
* https://releases.rancher.com/os/latest/rancheros-v0.5.0.tar.gz
|
||||
* https://releases.rancher.com/os/latest/rancheros-v0.6.1.tar.gz
|
||||
* https://releases.rancher.com/os/latest/rancheros.iso
|
||||
* https://releases.rancher.com/os/latest/rootfs_arm.tar.gz
|
||||
* https://releases.rancher.com/os/latest/rootfs_arm64.tar.gz
|
||||
* https://releases.rancher.com/os/latest/rootfs.tar.gz
|
||||
* https://releases.rancher.com/os/latest/vmlinuz
|
||||
|
||||
#### v0.5.0
|
||||
#### v0.6.1
|
||||
|
||||
* https://releases.rancher.com/os/v0.5.0/initrd
|
||||
* https://releases.rancher.com/os/v0.5.0/iso-checksums.txt
|
||||
* https://releases.rancher.com/os/v0.5.0/rancheros-openstack.img
|
||||
* https://releases.rancher.com/os/v0.5.0/rancheros-raspberry-pi.zip
|
||||
* https://releases.rancher.com/os/v0.5.0/rancheros-v0.5.0.tar.gz
|
||||
* https://releases.rancher.com/os/v0.5.0/rancheros.iso
|
||||
* https://releases.rancher.com/os/v0.5.0/rootfs_arm.tar.gz
|
||||
* https://releases.rancher.com/os/v0.5.0/vmlinuz
|
||||
* https://releases.rancher.com/os/v0.6.1/initrd
|
||||
* https://releases.rancher.com/os/v0.6.1/iso-checksums.txt
|
||||
* https://releases.rancher.com/os/v0.6.1/rancheros-openstack.img
|
||||
* https://releases.rancher.com/os/v0.6.1/rancheros-raspberry-pi.zip
|
||||
* https://releases.rancher.com/os/v0.6.1/rancheros-v0.6.1.tar.gz
|
||||
* https://releases.rancher.com/os/v0.6.1/rancheros.iso
|
||||
* https://releases.rancher.com/os/v0.6.1/rootfs_arm.tar.gz
|
||||
* https://releases.rancher.com/os/v0.6.1/rootfs_arm64.tar.gz
|
||||
* https://releases.rancher.com/os/v0.6.1/rootfs.tar.gz
|
||||
* https://releases.rancher.com/os/v0.6.1/vmlinuz
|
||||
|
||||
**Note**: you can use `http` instead of `https` in the above URLs, e.g. for iPXE.
|
||||
|
||||
@@ -57,25 +59,24 @@ SSH keys are added to the **`rancher`** user, so you must log in using the **ran
|
||||
|
||||
Region | Type | AMI |
|
||||
-------|------|------
|
||||
ap-northeast-1 | HVM | [ami-a452a2c5](https://console.aws.amazon.com/ec2/home?region=ap-northeast-1#launchInstanceWizard:ami=ami-a452a2c5)
|
||||
ap-northeast-2 | HVM | [ami-928e44fc](https://console.aws.amazon.com/ec2/home?region=ap-northeast-2#launchInstanceWizard:ami=ami-928e44fc)
|
||||
ap-southeast-1 | HVM | [ami-529f4231](https://console.aws.amazon.com/ec2/home?region=ap-southeast-1#launchInstanceWizard:ami=ami-529f4231)
|
||||
ap-southeast-2 | HVM | [ami-fc577c9f](https://console.aws.amazon.com/ec2/home?region=ap-southeast-2#launchInstanceWizard:ami=ami-fc577c9f)
|
||||
eu-central-1 | HVM | [ami-4a7f9425](https://console.aws.amazon.com/ec2/home?region=eu-central-1#launchInstanceWizard:ami=ami-4a7f9425)
|
||||
eu-west-1 | HVM | [ami-997b1eea](https://console.aws.amazon.com/ec2/home?region=eu-west-1#launchInstanceWizard:ami=ami-997b1eea)
|
||||
sa-east-1 | HVM | [ami-98198cf4](https://console.aws.amazon.com/ec2/home?region=sa-east-1#launchInstanceWizard:ami=ami-98198cf4)
|
||||
us-east-1 | HVM | [ami-1071ca07](https://console.aws.amazon.com/ec2/home?region=us-east-1#launchInstanceWizard:ami=ami-1071ca07)
|
||||
us-west-1 | HVM | [ami-a57730c5](https://console.aws.amazon.com/ec2/home?region=us-west-1#launchInstanceWizard:ami=ami-a57730c5)
|
||||
us-west-2 | HVM | [ami-f0f03190](https://console.aws.amazon.com/ec2/home?region=us-west-2#launchInstanceWizard:ami=ami-f0f03190)
|
||||
ap-south-1 | HVM | [ami-de97fdb1](https://console.aws.amazon.com/ec2/home?region=ap-south-1#launchInstanceWizard:ami=ami-de97fdb1)
|
||||
|
||||
### Google Compute Engine
|
||||
ap-northeast-1 | HVM | [ami-75954214](https://console.aws.amazon.com/ec2/home?region=ap-northeast-1#launchInstanceWizard:ami=ami-75954214)
|
||||
ap-northeast-2 | HVM | [ami-690dd807](https://console.aws.amazon.com/ec2/home?region=ap-northeast-2#launchInstanceWizard:ami=ami-690dd807)
|
||||
ap-south-1 | HVM | [ami-ed8cf982](https://console.aws.amazon.com/ec2/home?region=ap-south-1#launchInstanceWizard:ami=ami-ed8cf982)
|
||||
ap-southeast-1 | HVM | [ami-27bc6644](https://console.aws.amazon.com/ec2/home?region=ap-southeast-1#launchInstanceWizard:ami=ami-27bc6644)
|
||||
ap-southeast-2 | HVM | [ami-67172604](https://console.aws.amazon.com/ec2/home?region=ap-southeast-2#launchInstanceWizard:ami=ami-67172604)
|
||||
eu-central-1 | HVM | [ami-e88d7f87](https://console.aws.amazon.com/ec2/home?region=eu-central-1#launchInstanceWizard:ami=ami-e88d7f87)
|
||||
eu-west-1 | HVM | [ami-934837e0](https://console.aws.amazon.com/ec2/home?region=eu-west-1#launchInstanceWizard:ami=ami-934837e0)
|
||||
sa-east-1 | HVM | [ami-6949d905](https://console.aws.amazon.com/ec2/home?region=sa-east-1#launchInstanceWizard:ami=ami-6949d905)
|
||||
us-east-1 | HVM | [ami-a8d2a4bf](https://console.aws.amazon.com/ec2/home?region=us-east-1#launchInstanceWizard:ami=ami-a8d2a4bf)
|
||||
us-west-1 | HVM | [ami-fccb879c](https://console.aws.amazon.com/ec2/home?region=us-west-1#launchInstanceWizard:ami=ami-fccb879c)
|
||||
us-west-2 | HVM | [ami-1ed3007e](https://console.aws.amazon.com/ec2/home?region=us-west-2#launchInstanceWizard:ami=ami-1ed3007e)
|
||||
### Google Compute Engine
|
||||
|
||||
We are providing a disk image that users can download and import for use in Google Compute Engine. The image can be obtained from the release artifacts for RancherOS.
|
||||
|
||||
[Download Image](https://github.com/rancher/os/releases/download/v0.5.0/rancheros-v0.5.0.tar.gz)
|
||||
[Download Image](https://github.com/rancher/os/releases/download/v0.6.1/rancheros-v0.6.1.tar.gz)
|
||||
|
||||
Please follow the directions at our [docs to launch in GCE](http://docs.rancher.com/os/running-rancheros/cloud/gce/).
|
||||
Please follow the directions at our [docs to launch in GCE](http://docs.rancher.com/os/running-rancheros/cloud/gce/).
|
||||
|
||||
## Documentation for RancherOS
|
||||
|
||||
@@ -102,4 +103,3 @@ 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.
|
||||
|
||||
|
||||
@@ -1,24 +1,18 @@
|
||||
265,270d264
|
||||
< // Ensure only one update at a time checks resolv.conf.
|
||||
< if !conf.tryAcquireSema() {
|
||||
< return
|
||||
< }
|
||||
< defer conf.releaseSema()
|
||||
<
|
||||
276a271,280
|
||||
296a297,300
|
||||
> conf.update(name)
|
||||
> }
|
||||
>
|
||||
> func (conf *resolverConfig) update(name string) {
|
||||
> // Ensure only one update at a time checks resolv.conf.
|
||||
> if !conf.tryAcquireSema() {
|
||||
> return
|
||||
> }
|
||||
> defer conf.releaseSema()
|
||||
>
|
||||
293a298,302
|
||||
300a305,316
|
||||
> }
|
||||
>
|
||||
> func UpdateDnsConf() {
|
||||
> resolvConf.initOnce.Do(resolvConf.init)
|
||||
>
|
||||
> // Ensure only one update at a time checks resolv.conf.
|
||||
> if !resolvConf.tryAcquireSema() {
|
||||
> return
|
||||
> }
|
||||
> defer resolvConf.releaseSema()
|
||||
>
|
||||
> resolvConf.update("/etc/resolv.conf")
|
||||
|
||||
@@ -70,6 +70,8 @@ func ApplyConsole(cfg *rancherConfig.CloudConfig) {
|
||||
|
||||
if mount[2] == "swap" {
|
||||
cmd := exec.Command("swapon", device)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
log.Errorf("Unable to swapon %s: %v", device, err)
|
||||
@@ -85,6 +87,8 @@ func ApplyConsole(cfg *rancherConfig.CloudConfig) {
|
||||
cmdArgs = append(cmdArgs, "-o", mount[3])
|
||||
}
|
||||
cmd := exec.Command("mount", cmdArgs...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
if err := cmd.Run(); err != nil {
|
||||
log.Errorf("Failed to mount %s: %v", mount[1], err)
|
||||
}
|
||||
@@ -96,7 +100,6 @@ func ApplyConsole(cfg *rancherConfig.CloudConfig) {
|
||||
}
|
||||
|
||||
cmd := exec.Command(runcmd[0], runcmd[1:]...)
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
if err := cmd.Run(); err != nil {
|
||||
@@ -159,12 +162,21 @@ func applyPreConsole(cfg *rancherConfig.CloudConfig) {
|
||||
|
||||
func resizeDevice(cfg *rancherConfig.CloudConfig) error {
|
||||
cmd := exec.Command("growpart", cfg.Rancher.ResizeDevice, "1")
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Run()
|
||||
|
||||
cmd = exec.Command("partprobe", cfg.Rancher.ResizeDevice)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd = exec.Command("resize2fs", fmt.Sprintf("%s1", cfg.Rancher.ResizeDevice))
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package cloudinitsave
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -76,8 +77,8 @@ func enablePacketNetwork(cfg *rancherConfig.RancherConfig) {
|
||||
}
|
||||
|
||||
netCfg.Interfaces["bond0"] = bondCfg
|
||||
bytes, _ := yaml.Marshal(netCfg)
|
||||
logrus.Debugf("Generated network config: %s", string(bytes))
|
||||
b, _ := yaml.Marshal(netCfg)
|
||||
logrus.Debugf("Generated network config: %s", string(b))
|
||||
|
||||
cc := rancherConfig.CloudConfig{
|
||||
Rancher: rancherConfig.RancherConfig{
|
||||
@@ -85,6 +86,13 @@ func enablePacketNetwork(cfg *rancherConfig.RancherConfig) {
|
||||
},
|
||||
}
|
||||
|
||||
// Post to phone home URL on first boot
|
||||
if _, err = os.Stat(rancherConfig.CloudConfigNetworkFile); err != nil {
|
||||
if _, err = http.Post(m.PhoneHomeURL, "application/json", bytes.NewReader([]byte{})); err != nil {
|
||||
logrus.Errorf("Failed to post to Packet phone home URL: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(path.Dir(rancherConfig.CloudConfigNetworkFile), 0700); err != nil {
|
||||
logrus.Errorf("Failed to create directory for file %s: %v", rancherConfig.CloudConfigNetworkFile, err)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
"syscall"
|
||||
@@ -104,25 +105,14 @@ func Main() {
|
||||
|
||||
cloudinitexecute.ApplyConsole(cfg)
|
||||
|
||||
if util.ExistsAndExecutable(config.CloudConfigScriptFile) {
|
||||
cmd := exec.Command(config.CloudConfigScriptFile)
|
||||
if err := cmd.Run(); err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
if err := runScript(config.CloudConfigScriptFile); err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
|
||||
if util.ExistsAndExecutable(startScript) {
|
||||
cmd := exec.Command(startScript)
|
||||
if err := cmd.Run(); err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
if err := runScript(startScript); err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
|
||||
if util.ExistsAndExecutable("/etc/rc.local") {
|
||||
cmd := exec.Command("/etc/rc.local")
|
||||
if err := cmd.Run(); err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
if err := runScript("/etc/rc.local"); err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
|
||||
os.Setenv("TERM", "linux")
|
||||
@@ -180,9 +170,11 @@ func writeRespawn() error {
|
||||
files, err := ioutil.ReadDir("/etc/respawn.conf.d")
|
||||
if err == nil {
|
||||
for _, f := range files {
|
||||
content, err := ioutil.ReadFile(f.Name())
|
||||
p := path.Join("/etc/respawn.conf.d", f.Name())
|
||||
content, err := ioutil.ReadFile(p)
|
||||
if err != nil {
|
||||
return err
|
||||
log.Errorf("Failed to read %s: %v", p, err)
|
||||
continue
|
||||
}
|
||||
respawn += fmt.Sprintf("\n%s", string(content))
|
||||
}
|
||||
@@ -289,3 +281,29 @@ func setupSSH(cfg *config.CloudConfig) error {
|
||||
|
||||
return os.MkdirAll("/var/run/sshd", 0644)
|
||||
}
|
||||
|
||||
func runScript(path string) error {
|
||||
if !util.ExistsAndExecutable(path) {
|
||||
return nil
|
||||
}
|
||||
|
||||
script, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
magic := make([]byte, 2)
|
||||
if _, err = script.Read(magic); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd := exec.Command("/bin/sh", path)
|
||||
if string(magic) == "#!" {
|
||||
cmd = exec.Command(path)
|
||||
}
|
||||
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/codegangsta/cli"
|
||||
composeConfig "github.com/docker/libcompose/config"
|
||||
"github.com/docker/libcompose/project/options"
|
||||
"github.com/rancher/os/compose"
|
||||
"github.com/rancher/os/config"
|
||||
@@ -68,9 +67,7 @@ func engineSwitch(c *cli.Context) error {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
project.ServiceConfigs.Add("docker", &composeConfig.ServiceConfig{})
|
||||
|
||||
if err = compose.LoadService(project, cfg, true, newEngine); err != nil {
|
||||
if err = compose.LoadSpecialService(project, cfg, "docker", newEngine); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
|
||||
@@ -45,6 +46,10 @@ var installCommand = cli.Command{
|
||||
Name: "no-reboot",
|
||||
Usage: "do not reboot after install",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "append, a",
|
||||
Usage: "append additional kernel parameters",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -80,17 +85,18 @@ func installAction(c *cli.Context) error {
|
||||
cloudConfig = uc
|
||||
}
|
||||
|
||||
append := strings.TrimSpace(c.String("append"))
|
||||
force := c.Bool("force")
|
||||
reboot := !c.Bool("no-reboot")
|
||||
|
||||
if err := runInstall(image, installType, cloudConfig, device, force, reboot); err != nil {
|
||||
if err := runInstall(image, installType, cloudConfig, device, append, force, reboot); err != nil {
|
||||
log.WithFields(log.Fields{"err": err}).Fatal("Failed to run install")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func runInstall(image, installType, cloudConfig, device string, force, reboot bool) error {
|
||||
func runInstall(image, installType, cloudConfig, device, append string, force, reboot bool) error {
|
||||
in := bufio.NewReader(os.Stdin)
|
||||
|
||||
fmt.Printf("Installing from %s\n", image)
|
||||
@@ -109,14 +115,14 @@ func runInstall(image, installType, cloudConfig, device string, force, reboot bo
|
||||
return err
|
||||
}
|
||||
}
|
||||
cmd := exec.Command("system-docker", "run", "--net=host", "--privileged", "--volumes-from=user-volumes", image,
|
||||
"-d", device, "-t", installType, "-c", cloudConfig)
|
||||
cmd := exec.Command("system-docker", "run", "--net=host", "--privileged", "--volumes-from=user-volumes",
|
||||
"--volumes-from=command-volumes", image, "-d", device, "-t", installType, "-c", cloudConfig, "-a", append)
|
||||
cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr
|
||||
if err := cmd.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if reboot && yes(in, "Continue with reboot") {
|
||||
if reboot && (force || yes(in, "Continue with reboot")) {
|
||||
log.Info("Rebooting")
|
||||
power.Reboot()
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ func osSubcommands() []cli.Command {
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "append",
|
||||
Usage: "kernel args to append by kexec",
|
||||
Usage: "append additional kernel parameters",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "upgrade-console",
|
||||
@@ -187,11 +187,11 @@ func startUpgradeContainer(image string, stage, force, reboot, kexec bool, upgra
|
||||
|
||||
if kexec {
|
||||
command = append(command, "-k")
|
||||
}
|
||||
|
||||
kernelArgs = strings.TrimSpace(kernelArgs)
|
||||
if kernelArgs != "" {
|
||||
command = append(command, "-a", kernelArgs)
|
||||
}
|
||||
kernelArgs = strings.TrimSpace(kernelArgs)
|
||||
if kernelArgs != "" {
|
||||
command = append(command, "-a", kernelArgs)
|
||||
}
|
||||
|
||||
if upgradeConsole {
|
||||
|
||||
@@ -22,12 +22,6 @@ const (
|
||||
)
|
||||
|
||||
func Main() {
|
||||
if os.Getenv("DOCKER_CONF_SOURCED") == "" {
|
||||
if err := sourceDockerConf(os.Args); err != nil {
|
||||
log.Warnf("Failed to source %s: %v", dockerConf, err)
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
if _, err := os.Stat(consoleDone); err == nil {
|
||||
break
|
||||
@@ -70,16 +64,9 @@ func Main() {
|
||||
}
|
||||
|
||||
args := []string{
|
||||
"dockerlaunch",
|
||||
dockerBin,
|
||||
}
|
||||
|
||||
if len(os.Args) > 1 {
|
||||
args = append(args, os.Args[1:]...)
|
||||
}
|
||||
|
||||
if os.Getenv("DOCKER_OPTS") != "" {
|
||||
args = append(args, os.Getenv("DOCKER_OPTS"))
|
||||
"bash",
|
||||
"-c",
|
||||
fmt.Sprintf(`[ -e %s ] && source %s; exec /usr/bin/dockerlaunch %s %s $DOCKER_OPTS >> %s 2>&1`, dockerConf, dockerConf, dockerBin, strings.Join(os.Args[1:], " "), dockerLog),
|
||||
}
|
||||
|
||||
cfg := config.LoadConfig()
|
||||
@@ -88,16 +75,5 @@ func Main() {
|
||||
log.Error(err)
|
||||
}
|
||||
|
||||
log.Fatal(syscall.Exec("/usr/bin/dockerlaunch", args, os.Environ()))
|
||||
}
|
||||
|
||||
func sourceDockerConf(args []string) error {
|
||||
args = append([]string{
|
||||
"bash",
|
||||
"-c",
|
||||
fmt.Sprintf(`[ -e %s ] && source %s; exec docker-init "$@" >> %s 2>&1`, dockerConf, dockerConf, dockerLog),
|
||||
}, args...)
|
||||
env := os.Environ()
|
||||
env = append(env, "DOCKER_CONF_SOURCED=1")
|
||||
return syscall.Exec("/bin/bash", args, env)
|
||||
log.Fatal(syscall.Exec("/bin/bash", args, os.Environ()))
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"os"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
composeConfig "github.com/docker/libcompose/config"
|
||||
"github.com/docker/libcompose/project/options"
|
||||
"github.com/rancher/os/compose"
|
||||
"github.com/rancher/os/config"
|
||||
@@ -25,9 +24,7 @@ func Main() {
|
||||
}
|
||||
|
||||
if newConsole != "default" {
|
||||
project.ServiceConfigs.Add("console", &composeConfig.ServiceConfig{})
|
||||
|
||||
if err = compose.LoadService(project, cfg, true, newConsole); err != nil {
|
||||
if err = compose.LoadSpecialService(project, cfg, "console", newConsole); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,7 @@ import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path"
|
||||
"strconv"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
@@ -15,7 +13,6 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/engine-api/types"
|
||||
composeClient "github.com/docker/libcompose/docker/client"
|
||||
"github.com/docker/libcompose/project"
|
||||
"github.com/rancher/os/cmd/control"
|
||||
@@ -39,36 +36,13 @@ func Main() {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if err := syscall.Mount("/host/sys", "/sys", "", syscall.MS_BIND|syscall.MS_REC, ""); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
cfg := config.LoadConfig()
|
||||
|
||||
execID, resp, err := startDocker(cfg)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
process, err := getDockerProcess()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
handleTerm(process)
|
||||
|
||||
// Wait for Docker daemon to exit
|
||||
io.Copy(ioutil.Discard, resp.Reader)
|
||||
resp.Close()
|
||||
|
||||
client, err := rosDocker.NewSystemClient()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
state, err := client.ContainerExecInspect(context.Background(), execID)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Proxy exit code
|
||||
os.Exit(state.ExitCode)
|
||||
log.Fatal(startDocker(cfg))
|
||||
}
|
||||
|
||||
func copyBinaries(source, dest string) error {
|
||||
@@ -96,11 +70,27 @@ func copyBinaries(source, dest string) error {
|
||||
sourceFile := path.Join(source, file.Name())
|
||||
destFile := path.Join(dest, file.Name())
|
||||
|
||||
data, err := ioutil.ReadFile(sourceFile)
|
||||
in, err := os.Open(sourceFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ioutil.WriteFile(destFile, data, 0751); err != nil {
|
||||
out, err := os.Create(destFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = io.Copy(out, in); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = out.Sync(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = in.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = out.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.Chmod(destFile, 0751); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -138,7 +128,7 @@ func writeCerts(cfg *config.CloudConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func startDocker(cfg *config.CloudConfig) (string, types.HijackedResponse, error) {
|
||||
func startDocker(cfg *config.CloudConfig) error {
|
||||
storageContext := cfg.Rancher.Docker.StorageContext
|
||||
if storageContext == "" {
|
||||
storageContext = DEFAULT_STORAGE_CONTEXT
|
||||
@@ -148,23 +138,19 @@ func startDocker(cfg *config.CloudConfig) (string, types.HijackedResponse, error
|
||||
|
||||
p, err := compose.GetProject(cfg, true, false)
|
||||
if err != nil {
|
||||
return "", types.HijackedResponse{}, err
|
||||
return err
|
||||
}
|
||||
|
||||
pid, err := waitForPid(storageContext, p)
|
||||
if err != nil {
|
||||
return "", types.HijackedResponse{}, err
|
||||
return err
|
||||
}
|
||||
|
||||
log.Infof("%s PID %d", storageContext, pid)
|
||||
|
||||
client, err := rosDocker.NewSystemClient()
|
||||
if err != nil {
|
||||
return "", types.HijackedResponse{}, err
|
||||
}
|
||||
|
||||
if err := os.Remove(DOCKER_PID_FILE); err != nil && !os.IsNotExist(err) {
|
||||
return "", types.HijackedResponse{}, err
|
||||
return err
|
||||
}
|
||||
|
||||
dockerCfg := cfg.Rancher.Docker
|
||||
@@ -175,73 +161,23 @@ func startDocker(cfg *config.CloudConfig) (string, types.HijackedResponse, error
|
||||
|
||||
if dockerCfg.TLS {
|
||||
if err := writeCerts(cfg); err != nil {
|
||||
return "", types.HijackedResponse{}, err
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
cmd := []string{"env"}
|
||||
info, err := client.ContainerInspect(context.Background(), storageContext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd := []string{"docker-runc", "exec", "--", info.ID, "env"}
|
||||
log.Info(dockerCfg.AppendEnv())
|
||||
cmd = append(cmd, dockerCfg.AppendEnv()...)
|
||||
cmd = append(cmd, DOCKER_COMMAND)
|
||||
cmd = append(cmd, args...)
|
||||
log.Infof("Running %v", cmd)
|
||||
|
||||
resp, err := client.ContainerExecCreate(context.Background(), types.ExecConfig{
|
||||
Container: storageContext,
|
||||
Privileged: true,
|
||||
AttachStderr: true,
|
||||
AttachStdout: true,
|
||||
Cmd: cmd,
|
||||
})
|
||||
if err != nil {
|
||||
return "", types.HijackedResponse{}, err
|
||||
}
|
||||
|
||||
attachResp, err := client.ContainerExecAttach(context.Background(), resp.ID, types.ExecConfig{
|
||||
Detach: false,
|
||||
AttachStderr: true,
|
||||
AttachStdout: true,
|
||||
})
|
||||
if err != nil {
|
||||
return "", types.HijackedResponse{}, err
|
||||
}
|
||||
|
||||
return resp.ID, attachResp, nil
|
||||
}
|
||||
|
||||
func getDockerProcess() (*os.Process, error) {
|
||||
pidBytes, err := waitForFile(DOCKER_PID_FILE)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dockerPid, err := strconv.Atoi(string(pidBytes))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return os.FindProcess(dockerPid)
|
||||
}
|
||||
|
||||
func handleTerm(process *os.Process) {
|
||||
term := make(chan os.Signal)
|
||||
signal.Notify(term, syscall.SIGTERM)
|
||||
go func() {
|
||||
<-term
|
||||
process.Signal(syscall.SIGTERM)
|
||||
}()
|
||||
}
|
||||
|
||||
func waitForFile(file string) ([]byte, error) {
|
||||
for {
|
||||
contents, err := ioutil.ReadFile(file)
|
||||
if os.IsNotExist(err) {
|
||||
log.Infof("Waiting for %s", file)
|
||||
time.Sleep(1 * time.Second)
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return contents, nil
|
||||
}
|
||||
}
|
||||
return syscall.Exec("/usr/bin/ros", cmd, os.Environ())
|
||||
}
|
||||
|
||||
func waitForPid(service string, project *project.Project) (int, error) {
|
||||
|
||||
@@ -37,6 +37,37 @@ func LoadService(p *project.Project, cfg *config.CloudConfig, useNetwork bool, s
|
||||
return nil
|
||||
}
|
||||
|
||||
func LoadSpecialService(p *project.Project, cfg *config.CloudConfig, serviceName, serviceValue string) error {
|
||||
// Save config in case load fails
|
||||
previousConfig, ok := p.ServiceConfigs.Get(serviceName)
|
||||
|
||||
p.ServiceConfigs.Add(serviceName, &composeConfig.ServiceConfig{})
|
||||
|
||||
if err := LoadService(p, cfg, true, serviceValue); err != nil {
|
||||
// Rollback to previous config
|
||||
if ok {
|
||||
p.ServiceConfigs.Add(serviceName, previousConfig)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadConsoleService(cfg *config.CloudConfig, p *project.Project) error {
|
||||
if cfg.Rancher.Console == "" || cfg.Rancher.Console == "default" {
|
||||
return nil
|
||||
}
|
||||
return LoadSpecialService(p, cfg, "console", cfg.Rancher.Console)
|
||||
}
|
||||
|
||||
func loadEngineService(cfg *config.CloudConfig, p *project.Project) error {
|
||||
if cfg.Rancher.Docker.Engine == "" || cfg.Rancher.Docker.Engine == cfg.Rancher.Defaults.Docker.Engine {
|
||||
return nil
|
||||
}
|
||||
return LoadSpecialService(p, cfg, "docker", cfg.Rancher.Docker.Engine)
|
||||
}
|
||||
|
||||
func projectReload(p *project.Project, useNetwork *bool, loadConsole bool, environmentLookup *docker.ConfigEnvironment, authLookup *docker.ConfigAuthLookup) func() error {
|
||||
enabled := map[interface{}]interface{}{}
|
||||
return func() error {
|
||||
@@ -62,17 +93,18 @@ func projectReload(p *project.Project, useNetwork *bool, loadConsole bool, envir
|
||||
enabled[service] = service
|
||||
}
|
||||
|
||||
if loadConsole && cfg.Rancher.Console != "" && cfg.Rancher.Console != "default" {
|
||||
if err := LoadService(p, cfg, *useNetwork, cfg.Rancher.Console); err != nil && err != network.ErrNoNetwork {
|
||||
log.Error(err)
|
||||
if !*useNetwork {
|
||||
return nil
|
||||
}
|
||||
|
||||
if loadConsole {
|
||||
if err := loadConsoleService(cfg, p); err != nil {
|
||||
log.Errorf("Failed to load console: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.Rancher.Docker.Engine != "" && cfg.Rancher.Docker.Engine != cfg.Rancher.Defaults.Docker.Engine {
|
||||
p.ServiceConfigs.Add("docker", &composeConfig.ServiceConfig{})
|
||||
if err := LoadService(p, cfg, *useNetwork, cfg.Rancher.Docker.Engine); err != nil && err != network.ErrNoNetwork {
|
||||
log.Error(err)
|
||||
}
|
||||
if err := loadEngineService(cfg, p); err != nil {
|
||||
log.Errorf("Failed to load engine: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
yaml "github.com/cloudfoundry-incubator/candiedyaml"
|
||||
"testing"
|
||||
|
||||
yaml "github.com/cloudfoundry-incubator/candiedyaml"
|
||||
|
||||
"github.com/rancher/os/util"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
@@ -102,28 +103,60 @@ func TestUnmarshalOrReturnString(t *testing.T) {
|
||||
func TestParseCmdline(t *testing.T) {
|
||||
assert := require.New(t)
|
||||
|
||||
expected := map[interface{}]interface{}{
|
||||
assert.Equal(map[interface{}]interface{}{
|
||||
"rancher": map[interface{}]interface{}{
|
||||
"rescue": true,
|
||||
"key1": "value1",
|
||||
"key2": "value2",
|
||||
"keyArray": []interface{}{int64(1), int64(2)},
|
||||
"strArray": []interface{}{"url:http://192.168.1.100/cloud-config"},
|
||||
"obj1": map[interface{}]interface{}{
|
||||
"key3": "3value",
|
||||
"obj2": map[interface{}]interface{}{
|
||||
"key4": true,
|
||||
},
|
||||
},
|
||||
"key5": int64(5),
|
||||
"key6": "a,b",
|
||||
"key7": "a\nb",
|
||||
"key1": "value1",
|
||||
"key2": "value2",
|
||||
},
|
||||
}
|
||||
}, parseCmdline("a b rancher.key1=value1 c rancher.key2=value2"))
|
||||
|
||||
actual := parseCmdline("a b rancher.rescue rancher.keyArray=[1,2] rancher.strArray=[\"url:http://192.168.1.100/cloud-config\"] rancher.key1=value1 c rancher.key2=value2 rancher.obj1.key3=3value rancher.obj1.obj2.key4 rancher.key5=5 rancher.key6=a,b rancher.key7=a\nb")
|
||||
assert.Equal(map[interface{}]interface{}{
|
||||
"rancher": map[interface{}]interface{}{
|
||||
"key": "a,b",
|
||||
},
|
||||
}, parseCmdline("rancher.key=a,b"))
|
||||
|
||||
assert.Equal(expected, actual)
|
||||
assert.Equal(map[interface{}]interface{}{
|
||||
"rancher": map[interface{}]interface{}{
|
||||
"key": "a\nb",
|
||||
},
|
||||
}, parseCmdline("rancher.key=a\nb"))
|
||||
|
||||
assert.Equal(map[interface{}]interface{}{
|
||||
"rancher": map[interface{}]interface{}{
|
||||
"key": "a:b",
|
||||
},
|
||||
}, parseCmdline("rancher.key=a:b"))
|
||||
|
||||
assert.Equal(map[interface{}]interface{}{
|
||||
"rancher": map[interface{}]interface{}{
|
||||
"key": int64(5),
|
||||
},
|
||||
}, parseCmdline("rancher.key=5"))
|
||||
|
||||
assert.Equal(map[interface{}]interface{}{
|
||||
"rancher": map[interface{}]interface{}{
|
||||
"rescue": true,
|
||||
},
|
||||
}, parseCmdline("rancher.rescue"))
|
||||
|
||||
assert.Equal(map[interface{}]interface{}{
|
||||
"rancher": map[interface{}]interface{}{
|
||||
"keyArray": []interface{}{int64(1), int64(2)},
|
||||
},
|
||||
}, parseCmdline("rancher.keyArray=[1,2]"))
|
||||
|
||||
assert.Equal(map[interface{}]interface{}{
|
||||
"rancher": map[interface{}]interface{}{
|
||||
"strArray": []interface{}{"url:http://192.168.1.100/cloud-config"},
|
||||
},
|
||||
}, parseCmdline("rancher.strArray=[\"url:http://192.168.1.100/cloud-config\"]"))
|
||||
|
||||
assert.Equal(map[interface{}]interface{}{
|
||||
"rancher": map[interface{}]interface{}{
|
||||
"strArray": []interface{}{"url:http://192.168.1.100/cloud-config"},
|
||||
},
|
||||
}, parseCmdline("rancher.strArray=[url:http://192.168.1.100/cloud-config]"))
|
||||
}
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
@@ -202,12 +235,12 @@ func TestSet(t *testing.T) {
|
||||
}
|
||||
|
||||
type OuterData struct {
|
||||
One Data `"yaml:one"`
|
||||
One Data `yaml:"one"`
|
||||
}
|
||||
|
||||
type Data struct {
|
||||
Two bool `"yaml:two"`
|
||||
Three bool `"yaml:three"`
|
||||
Two bool `yaml:"two"`
|
||||
Three bool `yaml:"three"`
|
||||
}
|
||||
|
||||
func TestMapMerge(t *testing.T) {
|
||||
|
||||
@@ -116,9 +116,12 @@ func getOrSetVal(args string, data map[interface{}]interface{}, value interface{
|
||||
return "", tData
|
||||
}
|
||||
|
||||
// YAML parsers will remove newlines, but we'd like to keep those
|
||||
// replace newlines with magicString, and then undo after unmarshaling
|
||||
var magicString = "9XsJcx6dR5EERYCC"
|
||||
// Replace newlines and colons with random strings
|
||||
// This is done to avoid YAML treating these as special characters
|
||||
var (
|
||||
newlineMagicString = "9XsJcx6dR5EERYCC"
|
||||
colonMagicString = "V0Rc21pIVknMm2rr"
|
||||
)
|
||||
|
||||
func reverseReplacement(result interface{}) interface{} {
|
||||
switch val := result.(type) {
|
||||
@@ -133,14 +136,17 @@ func reverseReplacement(result interface{}) interface{} {
|
||||
}
|
||||
return val
|
||||
case string:
|
||||
return strings.Replace(val, magicString, "\n", -1)
|
||||
val = strings.Replace(val, newlineMagicString, "\n", -1)
|
||||
val = strings.Replace(val, colonMagicString, ":", -1)
|
||||
return val
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func unmarshalOrReturnString(value string) (result interface{}) {
|
||||
value = strings.Replace(value, "\n", magicString, -1)
|
||||
value = strings.Replace(value, "\n", newlineMagicString, -1)
|
||||
value = strings.Replace(value, ":", colonMagicString, -1)
|
||||
if err := yaml.Unmarshal([]byte(value), &result); err != nil {
|
||||
result = value
|
||||
}
|
||||
|
||||
@@ -96,19 +96,9 @@ func applyDebugFlags(rawCfg map[interface{}]interface{}) map[interface{}]interfa
|
||||
}
|
||||
|
||||
log.SetLevel(log.DebugLevel)
|
||||
if !util.Contains(cfg.Rancher.Docker.Args, "-D") {
|
||||
cfg.Rancher.Docker.Args = append(cfg.Rancher.Docker.Args, "-D")
|
||||
}
|
||||
if !util.Contains(cfg.Rancher.SystemDocker.Args, "-D") {
|
||||
cfg.Rancher.SystemDocker.Args = append(cfg.Rancher.SystemDocker.Args, "-D")
|
||||
}
|
||||
if !util.Contains(cfg.Rancher.BootstrapDocker.Args, "-D") {
|
||||
cfg.Rancher.BootstrapDocker.Args = append(cfg.Rancher.BootstrapDocker.Args, "-D")
|
||||
}
|
||||
|
||||
_, rawCfg = getOrSetVal("rancher.docker.args", rawCfg, cfg.Rancher.Docker.Args)
|
||||
_, rawCfg = getOrSetVal("rancher.system_docker.args", rawCfg, cfg.Rancher.SystemDocker.Args)
|
||||
_, rawCfg = getOrSetVal("rancher.bootstrap_docker.args", rawCfg, cfg.Rancher.BootstrapDocker.Args)
|
||||
_, rawCfg = getOrSetVal("rancher.docker.debug", rawCfg, true)
|
||||
_, rawCfg = getOrSetVal("rancher.system_docker.debug", rawCfg, true)
|
||||
_, rawCfg = getOrSetVal("rancher.bootstrap_docker.debug", rawCfg, true)
|
||||
_, rawCfg = getOrSetVal("rancher.log", rawCfg, true)
|
||||
|
||||
return rawCfg
|
||||
|
||||
@@ -1,17 +1,52 @@
|
||||
package config
|
||||
|
||||
import "os"
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/fatih/structs"
|
||||
)
|
||||
|
||||
func (d *DockerConfig) FullArgs() []string {
|
||||
args := append(d.Args, d.ExtraArgs...)
|
||||
|
||||
args := []string{"daemon"}
|
||||
args = append(args, generateEngineOptsSlice(d.EngineOpts)...)
|
||||
args = append(args, d.ExtraArgs...)
|
||||
if d.TLS {
|
||||
args = append(args, d.TLSArgs...)
|
||||
}
|
||||
|
||||
return args
|
||||
}
|
||||
|
||||
func (d *DockerConfig) AppendEnv() []string {
|
||||
return append(os.Environ(), d.Environment...)
|
||||
}
|
||||
|
||||
func generateEngineOptsSlice(opts EngineOpts) []string {
|
||||
optsStruct := structs.New(opts)
|
||||
|
||||
var optsSlice []string
|
||||
for k, v := range optsStruct.Map() {
|
||||
optTag := optsStruct.Field(k).Tag("opt")
|
||||
|
||||
switch value := v.(type) {
|
||||
case string:
|
||||
if value != "" {
|
||||
optsSlice = append(optsSlice, fmt.Sprintf("--%s", optTag), value)
|
||||
}
|
||||
case *bool:
|
||||
if value != nil {
|
||||
if *value {
|
||||
optsSlice = append(optsSlice, fmt.Sprintf("--%s", optTag))
|
||||
} else {
|
||||
optsSlice = append(optsSlice, fmt.Sprintf("--%s=false", optTag))
|
||||
}
|
||||
}
|
||||
case map[string]string:
|
||||
for k, v := range value {
|
||||
optsSlice = append(optsSlice, fmt.Sprintf("--%s", optTag), fmt.Sprintf("%s=%s", k, v))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return optsSlice
|
||||
}
|
||||
|
||||
48
config/docker_config_test.go
Normal file
48
config/docker_config_test.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func testContains(t *testing.T, s string, substrs ...string) {
|
||||
for _, substr := range substrs {
|
||||
if !strings.Contains(s, substr) {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateEngineOptsString(t *testing.T) {
|
||||
if len(generateEngineOptsSlice(EngineOpts{})) != 0 {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
testContains(t, fmt.Sprint(generateEngineOptsSlice(EngineOpts{
|
||||
Bridge: "bridge",
|
||||
})), "--bridge bridge")
|
||||
|
||||
testContains(t, fmt.Sprint(generateEngineOptsSlice(EngineOpts{
|
||||
SelinuxEnabled: &[]bool{true}[0],
|
||||
})), "--selinux-enabled")
|
||||
testContains(t, fmt.Sprint(generateEngineOptsSlice(EngineOpts{
|
||||
SelinuxEnabled: &[]bool{false}[0],
|
||||
})), "--selinux-enabled=false")
|
||||
|
||||
testContains(t, fmt.Sprint(generateEngineOptsSlice(EngineOpts{
|
||||
LogOpts: map[string]string{
|
||||
"max-size": "25m",
|
||||
"max-file": "2",
|
||||
},
|
||||
})), "--log-opt max-size=25m", "--log-opt max-file=2")
|
||||
|
||||
testContains(t, fmt.Sprint(generateEngineOptsSlice(EngineOpts{
|
||||
Bridge: "bridge",
|
||||
SelinuxEnabled: &[]bool{true}[0],
|
||||
LogOpts: map[string]string{
|
||||
"max-size": "25m",
|
||||
"max-file": "2",
|
||||
},
|
||||
})), "--bridge bridge", "--selinux-enabled", "--log-opt max-size=25m", "--log-opt max-file=2")
|
||||
}
|
||||
@@ -134,11 +134,31 @@ type UpgradeConfig struct {
|
||||
Rollback string `yaml:"rollback,omitempty"`
|
||||
}
|
||||
|
||||
type EngineOpts struct {
|
||||
Bridge string `yaml:"bridge,omitempty" opt:"bridge"`
|
||||
ConfigFile string `yaml:"config_file,omitempty" opt:"config-file"`
|
||||
Containerd string `yaml:"containerd,omitempty" opt:"containerd"`
|
||||
Debug *bool `yaml:"debug,omitempty" opt:"debug"`
|
||||
ExecRoot string `yaml:"exec_root,omitempty" opt:"exec-root"`
|
||||
Group string `yaml:"group,omitempty" opt:"group"`
|
||||
Graph string `yaml:"graph,omitempty" opt:"graph"`
|
||||
Host string `yaml:"host,omitempty" opt:"host"`
|
||||
LiveRestore *bool `yaml:"live_restore,omitempty" opt:"live-restore"`
|
||||
LogDriver string `yaml:"log_driver,omitempty" opt:"log-driver"`
|
||||
LogOpts map[string]string `yaml:"log_opts,omitempty" opt:"log-opt"`
|
||||
PidFile string `yaml:"pid_file,omitempty" opt:"pidfile"`
|
||||
RegistryMirror string `yaml:"registry_mirror,omitempty" opt:"registry-mirror"`
|
||||
Restart *bool `yaml:"restart,omitempty" opt:"restart"`
|
||||
SelinuxEnabled *bool `yaml:"selinux_enabled,omitempty" opt:"selinux-enabled"`
|
||||
StorageDriver string `yaml:"storage_driver,omitempty" opt:"storage-driver"`
|
||||
UserlandProxy *bool `yaml:"userland_proxy,omitempty" opt:"userland-proxy"`
|
||||
}
|
||||
|
||||
type DockerConfig struct {
|
||||
EngineOpts
|
||||
Engine string `yaml:"engine,omitempty"`
|
||||
TLS bool `yaml:"tls,omitempty"`
|
||||
TLSArgs []string `yaml:"tls_args,flow,omitempty"`
|
||||
Args []string `yaml:"args,flow,omitempty"`
|
||||
ExtraArgs []string `yaml:"extra_args,flow,omitempty"`
|
||||
ServerCert string `yaml:"server_cert,omitempty"`
|
||||
ServerKey string `yaml:"server_key,omitempty"`
|
||||
@@ -160,7 +180,6 @@ type StateConfig struct {
|
||||
Wait bool `yaml:"wait,omitempty"`
|
||||
Required bool `yaml:"required,omitempty"`
|
||||
Autoformat []string `yaml:"autoformat,omitempty"`
|
||||
FormatZero bool `yaml:"formatzero,omitempty"`
|
||||
MdadmScan bool `yaml:"mdadm_scan,omitempty"`
|
||||
Script string `yaml:"script,omitempty"`
|
||||
OemFsType string `yaml:"oem_fstype,omitempty"`
|
||||
|
||||
@@ -107,14 +107,14 @@ func (s *Service) shouldRebuild(ctx context.Context) (bool, error) {
|
||||
if newRebuildLabel == "always" {
|
||||
return true, nil
|
||||
}
|
||||
if s.Name() == "console" && cfg.Rancher.ForceConsoleRebuild {
|
||||
if err := config.Set("rancher.force_console_rebuild", false); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
if outOfSync {
|
||||
if s.Name() == "console" {
|
||||
if cfg.Rancher.ForceConsoleRebuild {
|
||||
if err := config.Set("rancher.force_console_rebuild", false); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
origConsoleLabel := containerInfo.Config.Labels[config.CONSOLE]
|
||||
newConsoleLabel := s.Config().Labels[config.CONSOLE]
|
||||
if newConsoleLabel != origConsoleLabel {
|
||||
|
||||
@@ -2,10 +2,7 @@
|
||||
set -ex
|
||||
|
||||
MAGIC=${MAGIC:-"boot2docker, please format-me"}
|
||||
|
||||
AUTOFORMAT=${AUTOFORMAT:-"/dev/sda /dev/vda"}
|
||||
DEVS=(${AUTOFORMAT})
|
||||
FORMATZERO=${FORMATZERO:-false}
|
||||
|
||||
for dev in ${DEVS[@]}; do
|
||||
if [ -b "${dev}" ]; then
|
||||
@@ -16,9 +13,6 @@ for dev in ${DEVS[@]}; do
|
||||
if [ "$HEADER" = "$MAGIC" ]; then
|
||||
# save the preload userdata.tar file
|
||||
dd if=${dev} of=/userdata.tar bs=1 count=8192
|
||||
elif [ "${FORMATZERO}" != "true" ]; then
|
||||
# do not try to guess whether to auto-format a disk beginning with 1MB filled with 00
|
||||
continue
|
||||
elif ! od -A d -N 1048576 ${dev} | head -n 3 | diff ./od-1m0 - >/dev/null 2>&1; then
|
||||
# do not auto-format if the disk does not begin with 1MB filled with 00
|
||||
continue
|
||||
|
||||
@@ -3,7 +3,6 @@ package init
|
||||
import (
|
||||
"syscall"
|
||||
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
@@ -18,9 +17,8 @@ func autoformat(cfg *config.CloudConfig) (*config.CloudConfig, error) {
|
||||
return cfg, nil
|
||||
}
|
||||
AUTOFORMAT := "AUTOFORMAT=" + strings.Join(cfg.Rancher.State.Autoformat, " ")
|
||||
FORMATZERO := "FORMATZERO=" + fmt.Sprint(cfg.Rancher.State.FormatZero)
|
||||
t := *cfg
|
||||
t.Rancher.Autoformat["autoformat"].Environment = []string{AUTOFORMAT, FORMATZERO}
|
||||
t.Rancher.Autoformat["autoformat"].Environment = []string{AUTOFORMAT}
|
||||
log.Info("Running Autoformat services")
|
||||
_, err := compose.RunServiceSet("autoformat", &t, t.Rancher.Autoformat)
|
||||
return &t, err
|
||||
|
||||
@@ -165,7 +165,7 @@ func tryMountAndBootstrap(cfg *config.CloudConfig) (*config.CloudConfig, error)
|
||||
func getLaunchConfig(cfg *config.CloudConfig, dockerCfg *config.DockerConfig) (*dockerlaunch.Config, []string) {
|
||||
var launchConfig dockerlaunch.Config
|
||||
|
||||
args := dockerlaunch.ParseConfig(&launchConfig, append(dockerCfg.Args, dockerCfg.ExtraArgs...)...)
|
||||
args := dockerlaunch.ParseConfig(&launchConfig, dockerCfg.FullArgs()...)
|
||||
|
||||
launchConfig.DnsConfig.Nameservers = cfg.Rancher.Defaults.Network.Dns.Nameservers
|
||||
launchConfig.DnsConfig.Search = cfg.Rancher.Defaults.Network.Dns.Search
|
||||
@@ -190,8 +190,8 @@ func setupSharedRoot(c *config.CloudConfig) (*config.CloudConfig, error) {
|
||||
}
|
||||
|
||||
if isInitrd() {
|
||||
for _, i := range []string{"/mnt", "/media"} {
|
||||
if err := os.Mkdir(i, 0755); err != nil {
|
||||
for _, i := range []string{"/mnt", "/media", "/var/lib/system-docker"} {
|
||||
if err := os.MkdirAll(i, 0755); err != nil {
|
||||
return c, err
|
||||
}
|
||||
if err := mount.Mount("tmpfs", i, "tmpfs", "rw"); err != nil {
|
||||
|
||||
@@ -4,8 +4,13 @@ rancher:
|
||||
SUFFIX: {{.SUFFIX}}
|
||||
defaults:
|
||||
hostname: {{.HOSTNAME_DEFAULT}}
|
||||
{{if eq "amd64" .ARCH -}}
|
||||
docker:
|
||||
engine: docker-1.12.1
|
||||
{{else -}}
|
||||
docker:
|
||||
engine: docker-1.11.2
|
||||
{{end -}}
|
||||
network:
|
||||
dns:
|
||||
nameservers: [8.8.8.8, 8.8.4.4]
|
||||
@@ -67,8 +72,13 @@ rancher:
|
||||
- /lib/firmware:/lib/firmware
|
||||
- /usr/bin/ros:/usr/bin/ros:ro
|
||||
bootstrap_docker:
|
||||
args: [daemon, -s, overlay, -b, none, --restart=false, -g, /var/lib/system-docker,
|
||||
-G, root, -H, 'unix:///var/run/system-docker.sock', --userland-proxy=false]
|
||||
bridge: none
|
||||
storage_driver: overlay
|
||||
restart: false
|
||||
graph: /var/lib/system-docker
|
||||
group: root
|
||||
host: "unix:///var/run/system-docker.sock"
|
||||
userland_proxy: false
|
||||
console: default
|
||||
cloud_init:
|
||||
datasources:
|
||||
@@ -135,8 +145,10 @@ rancher:
|
||||
uts: host
|
||||
privileged: true
|
||||
volumes_from:
|
||||
- command-volumes
|
||||
- system-volumes
|
||||
volumes:
|
||||
- /usr/bin/ros:/usr/bin/ros
|
||||
- /usr/bin/ros:/usr/bin/cloud-init-execute
|
||||
cloud-init-pre:
|
||||
image: {{.OS_REPO}}/os-cloudinit:{{.VERSION}}{{.SUFFIX}}
|
||||
environment:
|
||||
@@ -145,7 +157,7 @@ rancher:
|
||||
io.rancher.os.detach: "false"
|
||||
io.rancher.os.reloadconfig: "true"
|
||||
io.rancher.os.scope: system
|
||||
io.rancher.os.after: preload-system-images
|
||||
io.rancher.os.after: udev,preload-system-images
|
||||
net: host
|
||||
uts: host
|
||||
privileged: true
|
||||
@@ -323,8 +335,8 @@ rancher:
|
||||
udev-cold:
|
||||
image: {{.OS_REPO}}/os-udev:{{.VERSION}}{{.SUFFIX}}
|
||||
labels:
|
||||
io.rancher.os.detach: "false"
|
||||
io.rancher.os.scope: system
|
||||
io.rancher.os.before: udev
|
||||
net: host
|
||||
uts: host
|
||||
privileged: true
|
||||
@@ -338,6 +350,7 @@ rancher:
|
||||
labels:
|
||||
io.rancher.os.detach: "true"
|
||||
io.rancher.os.scope: system
|
||||
io.rancher.os.after: udev-cold
|
||||
net: host
|
||||
uts: host
|
||||
privileged: true
|
||||
@@ -359,7 +372,11 @@ rancher:
|
||||
- /home:/home
|
||||
- /opt:/opt
|
||||
docker:
|
||||
{{if eq "amd64" .ARCH -}}
|
||||
image: {{.OS_REPO}}/os-docker:1.12.1{{.SUFFIX}}
|
||||
{{else -}}
|
||||
image: {{.OS_REPO}}/os-docker:1.11.2{{.SUFFIX}}
|
||||
{{end -}}
|
||||
command: /usr/bin/user-docker
|
||||
environment:
|
||||
- HTTP_PROXY
|
||||
@@ -376,17 +393,37 @@ rancher:
|
||||
restart: always
|
||||
volumes_from:
|
||||
- all-volumes
|
||||
volumes:
|
||||
- /sys:/host/sys
|
||||
- /var/lib/system-docker:/var/lib/system-docker:shared
|
||||
system_docker:
|
||||
exec: true
|
||||
args: [daemon, --log-opt, max-size=25m, --log-opt, max-file=2, -s, overlay,
|
||||
--restart=false, -g, /var/lib/system-docker, -G, root,
|
||||
-p, /var/run/system-docker.pid, --exec-root=/var/run/system-docker, --config-file=/etc/docker/system-daemon.json,
|
||||
-H, 'unix:///var/run/system-docker.sock', --userland-proxy=false]
|
||||
storage_driver: overlay
|
||||
restart: false
|
||||
graph: /var/lib/system-docker
|
||||
group: root
|
||||
host: "unix:///var/run/system-docker.sock"
|
||||
pid_file: /var/run/system-docker.pid
|
||||
exec_root: /var/run/system-docker
|
||||
config_file: /etc/docker/system-docker.json
|
||||
userland_proxy: false
|
||||
log_opts:
|
||||
max-size: 25m
|
||||
max-file: 2
|
||||
upgrade:
|
||||
url: {{.OS_RELEASES_YML}}/releases{{.SUFFIX}}.yml
|
||||
image: {{.OS_REPO}}/os
|
||||
docker:
|
||||
{{if eq "amd64" .ARCH -}}
|
||||
engine: docker-1.12.1
|
||||
{{else -}}
|
||||
engine: docker-1.11.2
|
||||
{{end -}}
|
||||
storage_driver: overlay
|
||||
group: docker
|
||||
host: "unix:///var/run/docker.sock"
|
||||
log_opts:
|
||||
max-size: 25m
|
||||
max-file: 2
|
||||
tls_args: [--tlsverify, --tlscacert=/etc/docker/tls/ca.pem, --tlscert=/etc/docker/tls/server-cert.pem, --tlskey=/etc/docker/tls/server-key.pem,
|
||||
'-H=0.0.0.0:2376']
|
||||
args: [daemon, --log-opt, max-size=25m, --log-opt, max-file=2, -s, overlay, -G, docker, -H, 'unix:///var/run/docker.sock']
|
||||
|
||||
@@ -5,7 +5,7 @@ cd $(dirname $0)
|
||||
|
||||
./build
|
||||
./test
|
||||
#./validate
|
||||
./validate
|
||||
./prepare
|
||||
./package
|
||||
./integration-test
|
||||
|
||||
2
scripts/copy-latest.sh
Executable file
2
scripts/copy-latest.sh
Executable file
@@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
gsutil -m cp -r dist/artifacts/* gs://releases.rancher.com/os/latest
|
||||
3
scripts/copy-release.sh
Executable file
3
scripts/copy-release.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
. ./scripts/version
|
||||
gsutil -m cp -r dist/artifacts/* gs://releases.rancher.com/os/${VERSION}
|
||||
57
scripts/create-installed
Executable file
57
scripts/create-installed
Executable file
@@ -0,0 +1,57 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
cd $(dirname $0)/..
|
||||
|
||||
source ./scripts/run-common
|
||||
|
||||
INSTALLER=${BASE}/dist/artifacts/installer.tar
|
||||
|
||||
if [ ! -e ${INITRD} ]; then
|
||||
cp bin/ros ${INITRD_SRC}/usr/bin/ros
|
||||
./scripts/hash-initrd
|
||||
pushd ${INITRD_SRC} >/dev/null
|
||||
find . | cpio -H newc -o | gzip -1 > ${INITRD}
|
||||
popd >/dev/null
|
||||
fi
|
||||
|
||||
mkdir -p {dist,build/openstack/latest}
|
||||
|
||||
cat > build/openstack/latest/user_data << EOF
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
trap "poweroff" EXIT
|
||||
|
||||
sleep 5
|
||||
|
||||
mount -t 9p -o trans=virtio,version=9p2000.L config-2 /mnt
|
||||
|
||||
touch log
|
||||
openvt -s -- tail -f log &
|
||||
cat /mnt/installer.tar | system-docker load
|
||||
ros install -d /dev/vda -f --no-reboot >log 2>&1
|
||||
|
||||
touch /mnt/success
|
||||
EOF
|
||||
|
||||
rm -f build/{success,hd.img}
|
||||
qemu-img create -f qcow2 build/hd.img 8G
|
||||
cp ${INSTALLER} build/installer.tar
|
||||
qemu-system-${QEMUARCH} -serial stdio \
|
||||
-enable-kvm \
|
||||
-drive if=virtio,file=build/hd.img \
|
||||
-kernel ${KERNEL} \
|
||||
-initrd ${INITRD} \
|
||||
-m 2048 \
|
||||
-append "${DEFAULT_KERNEL_ARGS}" \
|
||||
-smp 1 \
|
||||
-nographic \
|
||||
-display none \
|
||||
-fsdev local,id=conf,security_model=none,path=$(pwd)/build \
|
||||
-device virtio-9p-pci,fsdev=conf,mount_tag=config-2
|
||||
|
||||
[ -f build/success ]
|
||||
|
||||
mkdir -p state
|
||||
cp build/hd.img state/hd.img
|
||||
@@ -1,10 +0,0 @@
|
||||
# Packet Support
|
||||
|
||||
Launch a Type-0, Type-1 or Type-3 Ubuntu 14.04 server and use the below cloud config. You can add any additional RancherOS configuration to it, but below is the bare minimum you need to provision RancherOS.
|
||||
|
||||
```yaml
|
||||
#cloud-config
|
||||
runcmd:
|
||||
- wget -O /tmp/cc https://raw.githubusercontent.com/rancher/os/master/scripts/hosting/packet/packet.sh
|
||||
- bash -x /tmp/cc
|
||||
```
|
||||
@@ -1,4 +0,0 @@
|
||||
#cloud-config
|
||||
runcmd:
|
||||
- wget -O /tmp/cc https://raw.githubusercontent.com/rancher/os/master/scripts/hosting/packet/packet.sh
|
||||
- bash -x /tmp/cc
|
||||
@@ -1,96 +1,124 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
if ros version >/dev/null 2>&1; then
|
||||
exit 0
|
||||
TINKERBELL_URL=$(cat /proc/cmdline | sed -e 's/^.*tinkerbell=//' -e 's/ .*$//')/phone-home
|
||||
INSTALLER_IMAGE=rancher/os:v0.7.0-rc3
|
||||
|
||||
tinkerbell_post()
|
||||
{
|
||||
system-docker run rancher/curl -X POST -H "Content-Type: application/json" -d "{\"type\":\"provisioning.$1\",\"body\":\"$2\"}" ${TINKERBELL_URL}
|
||||
}
|
||||
|
||||
tinkerbell_post 104 "Connected to magic install system"
|
||||
|
||||
DEV_PREFIX=/dev/sd
|
||||
if [ -e /dev/vda ]; then
|
||||
DEV_PREFIX=/dev/vd
|
||||
fi
|
||||
|
||||
apt-get install -y jq curl
|
||||
BOOT=${DEV_PREFIX}a1
|
||||
BOOT_TYPE=83
|
||||
SWAP=${DEV_PREFIX}a5
|
||||
SWAP_TYPE=82
|
||||
OEM=${DEV_PREFIX}a6
|
||||
OEM_TYPE=83
|
||||
ROOT=${DEV_PREFIX}a7
|
||||
ROOT_TYPE=83
|
||||
RAID=false
|
||||
|
||||
mkdir -p /boot/ros
|
||||
|
||||
URL_BASE=https://releases.rancher.com/os/latest
|
||||
curl -L $URL_BASE/vmlinuz > /boot/ros/vmlinuz
|
||||
curl -L $URL_BASE/initrd > /boot/ros/initrd
|
||||
|
||||
eval $(curl -sL https://metadata.packet.net/metadata | jq -r '.network.addresses[] | select(.address_family == 4 and .public) | "ADDRESS=\(.address)/\(.cidr)\nGATEWAY=\(.gateway)"')
|
||||
eval $(curl -sL https://metadata.packet.net/metadata | jq -r '.network.interfaces[0] | "MAC=\(.mac)"')
|
||||
|
||||
cat > /etc/default/grub << "EOF"
|
||||
# If you change this file, run 'update-grub' afterwards to update
|
||||
# /boot/grub/grub.cfg.
|
||||
# For full documentation of the options in this file, see:
|
||||
# info -f grub -n 'Simple configuration'
|
||||
|
||||
GRUB_DEFAULT=ROS
|
||||
GRUB_HIDDEN_TIMEOUT=15
|
||||
GRUB_HIDDEN_TIMEOUT_QUIET=false
|
||||
GRUB_TIMEOUT=10
|
||||
GRUB_DISTRIBUTOR=`lsb_release -i -s 2> /dev/null || echo Debian`
|
||||
GRUB_CMDLINE_LINUX_DEFAULT="console=tty0 console=ttyS1,115200n8"
|
||||
GRUB_CMDLINE_LINUX=""
|
||||
|
||||
# Uncomment to enable BadRAM filtering, modify to suit your needs
|
||||
# This works with Linux (no patch required) and with any kernel that obtains
|
||||
# the memory map information from GRUB (GNU Mach, kernel of FreeBSD ...)
|
||||
#GRUB_BADRAM="0x01234567,0xfefefefe,0x89abcdef,0xefefefef"
|
||||
|
||||
# Uncomment to disable graphical terminal (grub-pc only)
|
||||
GRUB_TERMINAL=console
|
||||
|
||||
# The resolution used on graphical terminal
|
||||
# note that you can use only modes which your graphic card supports via VBE
|
||||
# you can see them in real GRUB with the command `vbeinfo'
|
||||
#GRUB_GFXMODE=640x480
|
||||
|
||||
# Uncomment if you don't want GRUB to pass "root=UUID=xxx" parameter to Linux
|
||||
#GRUB_DISABLE_LINUX_UUID=true
|
||||
|
||||
# Uncomment to disable generation of recovery mode menu entries
|
||||
#GRUB_DISABLE_RECOVERY="true"
|
||||
|
||||
# Uncomment to get a beep at grub start
|
||||
#GRUB_INIT_TUNE="480 440 1"
|
||||
GRUB_TERMINAL=serial
|
||||
GRUB_SERIAL_COMMAND="serial --speed=115200 --unit=1 --word=8 --parity=no --stop=1"
|
||||
EOF
|
||||
|
||||
cat > /etc/grub.d/50ros << EOF
|
||||
#!/bin/sh
|
||||
exec tail -n +3 \$0
|
||||
# This file provides an easy way to add custom menu entries. Simply type the
|
||||
# menu entries you want to add after this comment. Be careful not to change
|
||||
# the 'exec tail' line above.
|
||||
menuentry 'ROS' {
|
||||
recordfail
|
||||
load_video
|
||||
insmod gzio
|
||||
insmod part_msdos
|
||||
insmod part_msdos
|
||||
insmod diskfilter
|
||||
insmod mdraid1x
|
||||
insmod ext2
|
||||
linux /ros/vmlinuz rancher.state.mdadm_scan rancher.state.directory=ros rancher.network.interfaces.bond0.address=$ADDRESS rancher.network.interfaces.bond0.gateway=$GATEWAY rancher.network.interfaces.mac:${MAC}.bond=bond0 rancher.cloud_init.datasources=[packet] rancher.rm_usr console=tty0 console=ttyS1,115200n8
|
||||
initrd /ros/initrd
|
||||
}
|
||||
menuentry 'ROS Debug' {
|
||||
recordfail
|
||||
load_video
|
||||
insmod gzio
|
||||
insmod part_msdos
|
||||
insmod part_msdos
|
||||
insmod diskfilter
|
||||
insmod mdraid1x
|
||||
insmod ext2
|
||||
linux /ros/vmlinuz rancher.state.mdadm_scan rancher.state.directory=ros rancher.network.interfaces.bond0.address=$ADDRESS rancher.network.interfaces.bond0.gateway=$GATEWAY rancher.network.interfaces.mac:${MAC}.bond=bond0 rancher.cloud_init.datasources=[packet] rancher.rm_usr rancher.network.interfaces.eth*.dhcp=false console=tty0 console=ttyS1,115200n8 rancher.debug rancher.log
|
||||
initrd /ros/initrd
|
||||
wait_for_dev()
|
||||
{
|
||||
for DEV; do
|
||||
for ((i=0;i<10;i++)); do
|
||||
if [ ! -e $DEV ]; then
|
||||
partprobe || true
|
||||
sleep 1
|
||||
else
|
||||
break
|
||||
fi
|
||||
done
|
||||
done
|
||||
}
|
||||
|
||||
modprobe md || true
|
||||
|
||||
dd if=/dev/zero of=${DEV_PREFIX}a bs=1M count=1
|
||||
|
||||
if [ -e ${DEV_PREFIX}b ]; then
|
||||
dd if=/dev/zero of=${DEV_PREFIX}b bs=1M count=1
|
||||
RAID=true
|
||||
BOOT=/dev/md1
|
||||
BOOT_TYPE=fd
|
||||
SWAP=/dev/md5
|
||||
SWAP_TYPE=fd
|
||||
OEM=/dev/md6
|
||||
OEM_TYPE=fd
|
||||
ROOT=/dev/md7
|
||||
ROOT_TYPE=fd
|
||||
fi
|
||||
|
||||
|
||||
# Partition BOOT
|
||||
echo -e "n\np\n1\n\n+2G\nt\n${BOOT_TYPE}\nw" | fdisk ${DEV_PREFIX}a || true
|
||||
partprobe || true
|
||||
# Partition Extended
|
||||
echo -e "n\ne\n\n\n\nw" | fdisk ${DEV_PREFIX}a || true
|
||||
partprobe || true
|
||||
# Partition SWAP
|
||||
echo -e "n\nl\n\n+2G\n\nt\n5\n${SWAP_TYPE}\nw" | fdisk ${DEV_PREFIX}a || true
|
||||
partprobe || true
|
||||
# Partition OEM
|
||||
echo -e "n\nl\n\n+100M\n\nt\n6\n${OEM_TYPE}\nw" | fdisk ${DEV_PREFIX}a || true
|
||||
partprobe || true
|
||||
# Partition ROOT
|
||||
echo -e "n\nl\n\n\n\nt\n7\n${ROOT_TYPE}\nw" | fdisk ${DEV_PREFIX}a || true
|
||||
partprobe || true
|
||||
# Make boot active
|
||||
echo -e "a\n1\nw" | fdisk ${DEV_PREFIX}a || true
|
||||
partprobe || true
|
||||
|
||||
if [ "$RAID" = "true" ]; then
|
||||
sfdisk --dump ${DEV_PREFIX}a | sfdisk --no-reread ${DEV_PREFIX}b
|
||||
|
||||
wait_for_dev ${DEV_PREFIX}b1 ${DEV_PREFIX}b5 ${DEV_PREFIX}b6 ${DEV_PREFIX}b7 ${DEV_PREFIX}a1 ${DEV_PREFIX}a5 ${DEV_PREFIX}a6 ${DEV_PREFIX}a7
|
||||
|
||||
mdadm --create $BOOT --level=1 --metadata=1.0 --raid-devices=2 ${DEV_PREFIX}a1 ${DEV_PREFIX}b1
|
||||
mdadm --create $SWAP --level=1 --metadata=1.2 --raid-devices=2 ${DEV_PREFIX}a5 ${DEV_PREFIX}b5
|
||||
mdadm --create $OEM --level=1 --metadata=1.2 --raid-devices=2 ${DEV_PREFIX}a6 ${DEV_PREFIX}b6
|
||||
mdadm --create $ROOT --level=1 --metadata=1.2 --raid-devices=2 ${DEV_PREFIX}a7 ${DEV_PREFIX}b7
|
||||
fi
|
||||
|
||||
mkswap -L RANCHER_SWAP $SWAP
|
||||
system-docker run --privileged --entrypoint /bin/bash ${INSTALLER_IMAGE} -c "mkfs.ext4 -L RANCHER_BOOT $BOOT"
|
||||
system-docker run --privileged --entrypoint /bin/bash ${INSTALLER_IMAGE} -c "mkfs.ext4 -L RANCHER_STATE $ROOT"
|
||||
system-docker run --privileged --entrypoint /bin/bash ${INSTALLER_IMAGE} -c "mkfs.ext4 -L RANCHER_OEM $OEM"
|
||||
|
||||
tinkerbell_post 105 "Server partitions created"
|
||||
|
||||
mkdir -p /mnt/oem
|
||||
mount $(ros dev LABEL=RANCHER_OEM) /mnt/oem
|
||||
cat > /mnt/oem/oem-config.yml << EOF
|
||||
#cloud-config
|
||||
mounts:
|
||||
- [ LABEL=RANCHER_SWAP, "", swap, "" ]
|
||||
EOF
|
||||
umount /mnt/oem
|
||||
|
||||
chmod +x /etc/grub.d/50ros
|
||||
tinkerbell_post 106 "OEM drive configured"
|
||||
|
||||
update-grub2
|
||||
METADATA=$(system-docker run rancher/curl -sL https://metadata.packet.net/metadata)
|
||||
eval $(echo ${METADATA} | jq -r '.network.addresses[] | select(.address_family == 4 and .public) | "ADDRESS=\(.address)/\(.cidr)\nGATEWAY=\(.gateway)"')
|
||||
eval $(echo ${METADATA} | jq -r '.network.interfaces[0] | "MAC=\(.mac)"')
|
||||
NETWORK_ARGS="rancher.network.interfaces.bond0.address=$ADDRESS rancher.network.interfaces.bond0.gateway=$GATEWAY rancher.network.interfaces.mac:${MAC}.bond=bond0"
|
||||
|
||||
tune2fs -L RANCHER_STATE $(df -h / | sed 1d | awk '{print $1}')
|
||||
tinkerbell_post 107 "Network interface configuration fetched from metadata"
|
||||
|
||||
COMMON_ARGS="console=ttyS1,115200n8 rancher.autologin=ttyS1 rancher.cloud_init.datasources=[packet] ${NETWORK_ARGS}"
|
||||
if [ "$RAID" = "true" ]; then
|
||||
ros install -f -t raid -i ${INSTALLER_IMAGE} -d ${DEV_PREFIX}a -a "rancher.state.mdadm_scan ${COMMON_ARGS}" --no-reboot
|
||||
else
|
||||
ros install -f -t noformat -i ${INSTALLER_IMAGE} -d ${DEV_PREFIX}a -a "${COMMON_ARGS}" --no-reboot
|
||||
fi
|
||||
|
||||
tinkerbell_post 108 "Installation finished, rebooting server"
|
||||
reboot
|
||||
|
||||
@@ -14,6 +14,7 @@ trap "poweroff" EXIT
|
||||
mount -t 9p -o trans=virtio,version=9p2000.L config-2 /mnt
|
||||
|
||||
touch log
|
||||
sleep 5
|
||||
openvt -s -- tail -f log &
|
||||
ros install -d /dev/vda -f --no-reboot >log 2>&1
|
||||
|
||||
@@ -25,7 +26,7 @@ qemu-img create -f qcow2 build/hd.img 8G
|
||||
kvm -curses \
|
||||
-drive if=virtio,file=build/hd.img \
|
||||
-cdrom assets/rancheros.iso \
|
||||
-m 1024 \
|
||||
-m 2048 \
|
||||
-fsdev local,id=conf,security_model=none,path=$(pwd)/build \
|
||||
-device virtio-9p-pci,fsdev=conf,mount_tag=config-2
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
FROM ubuntu:16.04
|
||||
ENV DEBIAN_FRONTEND noninteractive
|
||||
RUN apt-get update && \
|
||||
apt-get install --no-install-recommends -y udev grub2 parted kexec-tools && \
|
||||
apt-get install --no-install-recommends -y udev grub2 parted kexec-tools extlinux syslinux-common && \
|
||||
rm -rf /var/lib/apt/*
|
||||
|
||||
COPY ./build/vmlinuz ./build/initrd /dist/
|
||||
|
||||
@@ -27,9 +27,11 @@ DIST=${DIST:-/dist}
|
||||
CLOUD_CONFIG=${CLOUD_CONFIG:-"${SCRIPTS_DIR}/conf/empty.yml"}
|
||||
CONSOLE=tty0
|
||||
BASE_DIR="/mnt/new_img"
|
||||
BOOT=boot/
|
||||
# TODO: Change this to a number so that users can specify.
|
||||
# Will need to make it so that our builds and packer APIs remain consistent.
|
||||
PARTITION=${PARTITION:=${DEVICE}1}
|
||||
KERNEL_ARGS="rancher.state.dev=LABEL=RANCHER_STATE rancher.state.wait console=${CONSOLE}"
|
||||
|
||||
device_defined()
|
||||
{
|
||||
@@ -45,18 +47,28 @@ format_device()
|
||||
mkfs.ext4 -F -i 4096 -L RANCHER_STATE ${PARTITION}
|
||||
}
|
||||
|
||||
mount_device()
|
||||
get_dev()
|
||||
{
|
||||
local label=RANCHER_STATE
|
||||
if [ -z "$(which ros)" ]; then
|
||||
lsblk -n -o label $1
|
||||
else
|
||||
ros dev LABEL=${1}
|
||||
fi
|
||||
}
|
||||
|
||||
mount_device()
|
||||
{
|
||||
LABEL=RANCHER_STATE
|
||||
local raw="${1:-false}"
|
||||
|
||||
mkdir -p ${BASE_DIR}
|
||||
|
||||
if [ "$(lsblk -o label|grep RANCHER_BOOT | wc -l)" -gt "0" ]; then
|
||||
label=RANCHER_BOOT
|
||||
if [ -n "$(get_dev RANCHER_BOOT)" ]; then
|
||||
LABEL=RANCHER_BOOT
|
||||
BOOT=
|
||||
fi
|
||||
|
||||
local mount_opts="-L ${label}"
|
||||
|
||||
local mount_opts="-L ${LABEL}"
|
||||
if [ "${raw}" == "true" ]; then
|
||||
device_defined ${DEVICE}
|
||||
mount_opts=${PARTITION}
|
||||
@@ -68,7 +80,21 @@ mount_device()
|
||||
|
||||
create_boot_dirs()
|
||||
{
|
||||
mkdir -p ${BASE_DIR}/boot/grub
|
||||
mkdir -p ${BASE_DIR}/${BOOT}grub
|
||||
mkdir -p ${BASE_DIR}/${BOOT}syslinux
|
||||
}
|
||||
|
||||
install_syslinux() {
|
||||
dd bs=440 count=1 if=/usr/lib/syslinux/mbr/mbr.bin of=${DEVICE}
|
||||
cp /usr/lib/syslinux/modules/bios/* ${BASE_DIR}/${BOOT}syslinux
|
||||
extlinux --install ${BASE_DIR}/${BOOT}syslinux
|
||||
}
|
||||
|
||||
install_syslinux_raid() {
|
||||
dd bs=440 count=1 if=/usr/lib/syslinux/mbr/mbr.bin of=/dev/sda
|
||||
dd bs=440 count=1 if=/usr/lib/syslinux/mbr/mbr.bin of=/dev/sdb
|
||||
cp /usr/lib/syslinux/modules/bios/* ${BASE_DIR}/${BOOT}syslinux
|
||||
extlinux --install --raid ${BASE_DIR}/${BOOT}syslinux
|
||||
}
|
||||
|
||||
install_grub() {
|
||||
@@ -76,7 +102,7 @@ install_grub() {
|
||||
}
|
||||
|
||||
grub2_config(){
|
||||
local grub_cfg=${BASE_DIR}/boot/grub/grub.cfg
|
||||
local grub_cfg=${BASE_DIR}/${BOOT}grub/grub.cfg
|
||||
local append_line="${1}"
|
||||
cat >${grub_cfg} <<EOF
|
||||
set default="0"
|
||||
@@ -85,8 +111,8 @@ set timeout="1"
|
||||
|
||||
menuentry "RancherOS-current" {
|
||||
set root=(hd0,msdos1)
|
||||
linux /boot/vmlinuz-${VERSION}-rancheros ${append_line} rancher.state.dev=LABEL=RANCHER_STATE rancher.state.wait console=${CONSOLE}
|
||||
initrd /boot/initrd-${VERSION}-rancheros
|
||||
linux /${BOOT}vmlinuz-${VERSION}-rancheros ${KERNEL_ARGS} ${append_line}
|
||||
initrd /${BOOT}initrd-${VERSION}-rancheros
|
||||
}
|
||||
|
||||
EOF
|
||||
@@ -96,23 +122,48 @@ if [ ! -z ${ROLLBACK_VERSION} ]; then
|
||||
cat >>${grub_cfg} <<EOF
|
||||
menuentry "RancherOS-rollback" {
|
||||
set root=(hd0,msdos1)
|
||||
linux /boot/vmlinuz-${ROLLBACK_VERSION}-rancheros ${append_line} rancher.state.dev=LABEL=RANCHER_STATE rancher.state.wait console=${CONSOLE}
|
||||
initrd /boot/initrd-${ROLLBACK_VERSION}-rancheros
|
||||
linux /${BOOT}vmlinuz-${ROLLBACK_VERSION}-rancheros ${KERNEL_ARGS} ${append_line}
|
||||
initrd /${BOOT}initrd-${ROLLBACK_VERSION}-rancheros
|
||||
}
|
||||
EOF
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
syslinux_config(){
|
||||
local syslinux_cfg=${BASE_DIR}/${BOOT}syslinux/syslinux.cfg
|
||||
local append_line="${1}"
|
||||
cat >${syslinux_cfg} <<EOF
|
||||
DEFAULT RancherOS-current
|
||||
|
||||
LABEL RancherOS-current
|
||||
LINUX ../vmlinuz-${VERSION}-rancheros
|
||||
APPEND ${KERNEL_ARGS} ${append_line}
|
||||
INITRD ../initrd-${VERSION}-rancheros
|
||||
|
||||
EOF
|
||||
|
||||
if [ ! -z ${ROLLBACK_VERSION} ]; then
|
||||
cat >>${syslinux_cfg} <<EOF
|
||||
LABEL RancherOS-rollback
|
||||
LINUX ../vmlinuz-${ROLLBACK_VERSION}-rancheros
|
||||
APPEND ${KERNEL_ARGS} ${append_line}
|
||||
INITRD ../initrd-${ROLLBACK_VERSION}-rancheros
|
||||
|
||||
EOF
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
install_rancher()
|
||||
{
|
||||
cp ${DIST}/initrd ${BASE_DIR}/boot/initrd-${VERSION}-rancheros
|
||||
cp ${DIST}/vmlinuz ${BASE_DIR}/boot/vmlinuz-${VERSION}-rancheros
|
||||
cp ${DIST}/initrd ${BASE_DIR}/${BOOT}initrd-${VERSION}-rancheros
|
||||
cp ${DIST}/vmlinuz ${BASE_DIR}/${BOOT}vmlinuz-${VERSION}-rancheros
|
||||
}
|
||||
|
||||
pvgrub_config()
|
||||
{
|
||||
local grub_file=${BASE_DIR}/boot/grub/menu.lst
|
||||
local grub_file=${BASE_DIR}/${BOOT}grub/menu.lst
|
||||
local append_line="${1}"
|
||||
cat > ${grub_file}<<EOF
|
||||
default 0
|
||||
@@ -123,8 +174,8 @@ hiddenmenu
|
||||
|
||||
title RancherOS ${VERSION}-(current)
|
||||
root (hd0)
|
||||
kernel /boot/vmlinuz-${VERSION}-rancheros ${append_line} rancher.state.dev=LABEL=RANCHER_STATE rancher.state.wait console=${CONSOLE}
|
||||
initrd /boot/initrd-${VERSION}-rancheros
|
||||
kernel /${BOOT}vmlinuz-${VERSION}-rancheros ${KERNEL_ARGS} ${append_line}
|
||||
initrd /${BOOT}initrd-${VERSION}-rancheros
|
||||
|
||||
EOF
|
||||
|
||||
@@ -133,8 +184,8 @@ if [ ! -z ${ROLLBACK_VERSION} ]; then
|
||||
cat >> ${grub_file}<<EOF
|
||||
title RancherOS ${ROLLBACK_VERSION}-(rollback)
|
||||
root (hd0)
|
||||
kernel /boot/vmlinuz-${ROLLBACK_VERSION}-rancheros ${append_line} rancher.state.dev=LABEL=RANCHER_STATE rancher.state.wait console=${CONSOLE}
|
||||
initrd /boot/initrd-${ROLLBACK_VERSION}-rancheros
|
||||
kernel /${BOOT}/vmlinuz-${ROLLBACK_VERSION}-rancheros ${KERNEL_ARGS} ${append_line}
|
||||
initrd /${BOOT}initrd-${ROLLBACK_VERSION}-rancheros
|
||||
EOF
|
||||
fi
|
||||
}
|
||||
@@ -146,7 +197,6 @@ format_and_mount()
|
||||
create_boot_dirs
|
||||
}
|
||||
|
||||
KERNEL_ARGS=${KERNEL_ARGS:-""}
|
||||
if [ -n ${ENV} ]; then
|
||||
case ${ENV} in
|
||||
"generic")
|
||||
@@ -173,6 +223,16 @@ if [ -n ${ENV} ]; then
|
||||
install_grub
|
||||
"${SCRIPTS_DIR}/seed-data" ${BASE_DIR} ${CLOUD_CONFIG} ${FILES}
|
||||
;;
|
||||
"noformat")
|
||||
mount_device
|
||||
create_boot_dirs
|
||||
install_syslinux
|
||||
;;
|
||||
"raid")
|
||||
mount_device
|
||||
create_boot_dirs
|
||||
install_syslinux_raid
|
||||
;;
|
||||
"bootstrap")
|
||||
CONSOLE=ttyS0
|
||||
mount_device true
|
||||
@@ -180,7 +240,7 @@ if [ -n ${ENV} ]; then
|
||||
KERNEL_ARGS="${KERNEL_ARGS} rancher.cloud_init.datasources=[ec2,gce]"
|
||||
;;
|
||||
"rancher-upgrade")
|
||||
mount_device
|
||||
mount_device
|
||||
create_boot_dirs
|
||||
;;
|
||||
*)
|
||||
@@ -190,8 +250,17 @@ if [ -n ${ENV} ]; then
|
||||
esac
|
||||
fi
|
||||
|
||||
grub2_config "${KERNEL_ARGS}"
|
||||
pvgrub_config "${KERNEL_ARGS}"
|
||||
if [ -e ${BASE_DIR}/${BOOT}append ]; then
|
||||
PRESERVED_APPEND=$(cat ${BASE_DIR}/${BOOT}append)
|
||||
fi
|
||||
if [ "${APPEND}" = "" ]; then
|
||||
APPEND="${PRESERVED_APPEND}"
|
||||
fi
|
||||
echo "${APPEND}" > ${BASE_DIR}/${BOOT}append
|
||||
|
||||
grub2_config "${APPEND}"
|
||||
syslinux_config "${APPEND}"
|
||||
pvgrub_config "${APPEND}"
|
||||
install_rancher
|
||||
|
||||
seusers=${BASE_DIR}/etc/selinux/ros/seusers
|
||||
@@ -204,8 +273,5 @@ if [ -f "${failsafe_context}" ]; then
|
||||
fi
|
||||
|
||||
if [ "$KEXEC" = "y" ]; then
|
||||
if [ "$APPEND" = "" ]; then
|
||||
APPEND=$(cat /proc/cmdline)
|
||||
fi
|
||||
kexec -l ${DIST}/vmlinuz --initrd=${DIST}/initrd --append="$APPEND" -f
|
||||
kexec -l ${DIST}/vmlinuz --initrd=${DIST}/initrd --append="${KERNEL_ARGS} ${APPEND}" -f
|
||||
fi
|
||||
|
||||
@@ -14,4 +14,4 @@ if [ ! -e ../dist/artifacts/initrd ]; then
|
||||
../scripts/dev
|
||||
fi
|
||||
|
||||
go test -timeout 20m
|
||||
go test -timeout 30m
|
||||
|
||||
@@ -26,6 +26,11 @@ ln -s ros ${INITRD_DIR}/usr/bin/docker-runc
|
||||
ln -s ../../../../usr/bin/ros ${INITRD_DIR}/usr/var/lib/cni/bin/bridge
|
||||
ln -s ../../../../usr/bin/ros ${INITRD_DIR}/usr/var/lib/cni/bin/host-local
|
||||
|
||||
# Support upgrades from old persistent consoles that bind mount these
|
||||
touch ${INITRD_DIR}/usr/bin/docker-containerd
|
||||
touch ${INITRD_DIR}/usr/bin/docker-containerd-shim
|
||||
touch ${INITRD_DIR}/usr/bin/docker
|
||||
|
||||
if [ -e ${DOWNLOADS}/kernel.tar.gz ]; then
|
||||
mkdir -p ${BUILD}/kernel
|
||||
tar xf ${DOWNLOADS}/kernel.tar.gz -C ${BUILD}/kernel
|
||||
|
||||
@@ -16,5 +16,6 @@ cp ./dist/artifacts/{initrd,vmlinuz} ./scripts/installer/build
|
||||
trap "rm -rf ./scripts/installer/build" EXIT
|
||||
|
||||
docker build -t ${OS_REPO}/os:${VERSION}${SUFFIX} --build-arg VERSION=${VERSION} -f $DOCKERFILE ./scripts/installer
|
||||
docker save -o dist/artifacts/installer.tar ${OS_REPO}/os:${VERSION}${SUFFIX}
|
||||
echo ${OS_REPO}/os:${VERSION}${SUFFIX} > dist/images
|
||||
echo Built ${OS_REPO}/os:${VERSION}${SUFFIX}
|
||||
|
||||
@@ -18,7 +18,7 @@ mkdir -p ${ARTIFACTS} ${PREPOP_DIR}
|
||||
if [ "$(docker info | grep 'Storage Driver: ' | sed 's/Storage Driver: //')" != "overlay" ]; then
|
||||
echo Overlay storage driver is require to prepackage exploded images
|
||||
echo packaging images.tar instead
|
||||
tar czf ${ARTIFACTS}/rootfs.tar.gz --exclude lib/modules --exclude lib/firmware -C ${INITRD_DIR} .
|
||||
tar czf ${ARTIFACTS}/rootfs${SUFFIX}.tar.gz --exclude lib/modules --exclude lib/firmware -C ${INITRD_DIR} .
|
||||
exit 0
|
||||
fi
|
||||
|
||||
@@ -26,10 +26,10 @@ DFS=$(docker run -d --privileged -v /lib/modules/$(uname -r):/lib/modules/$(unam
|
||||
trap "docker rm -fv ${DFS_ARCH} ${DFS}" EXIT
|
||||
docker exec -i ${DFS} docker load < ${INITRD_DIR}/usr/share/ros/images.tar
|
||||
docker stop ${DFS}
|
||||
docker run --rm --volumes-from=${DFS} busybox tar -c -C /var/lib/docker ./image | tar -x -C ${PREPOP_DIR}
|
||||
docker run --rm --volumes-from=${DFS} busybox tar -c -C /var/lib/docker ./overlay | tar -x -C ${PREPOP_DIR}
|
||||
docker run --rm --volumes-from=${DFS} --entrypoint /bin/bash rancher/os-base -c "tar -c -C /var/lib/docker ./image" | tar -x -C ${PREPOP_DIR}
|
||||
docker run --rm --volumes-from=${DFS} --entrypoint /bin/bash rancher/os-base -c "tar -c -C /var/lib/docker ./overlay" | tar -x -C ${PREPOP_DIR}
|
||||
|
||||
tar -cf ${ARTIFACTS}/rootfs.tar --exclude usr/share/ros/images.tar --exclude lib/modules --exclude lib/firmware -C ${INITRD_DIR} .
|
||||
tar -rf ${ARTIFACTS}/rootfs.tar -C ${IMAGE_CACHE} .
|
||||
rm -f ${ARTIFACTS}/rootfs.tar.gz
|
||||
gzip ${ARTIFACTS}/rootfs.tar
|
||||
tar -cf ${ARTIFACTS}/rootfs${SUFFIX}.tar --exclude usr/share/ros/images.tar --exclude lib/modules --exclude lib/firmware -C ${INITRD_DIR} .
|
||||
tar -rf ${ARTIFACTS}/rootfs${SUFFIX}.tar -C ${IMAGE_CACHE} .
|
||||
rm -f ${ARTIFACTS}/rootfs${SUFFIX}.tar.gz
|
||||
gzip ${ARTIFACTS}/rootfs${SUFFIX}.tar
|
||||
|
||||
119
scripts/run
119
scripts/run
@@ -4,47 +4,7 @@ set -e
|
||||
|
||||
cd $(dirname $0)/..
|
||||
|
||||
HOST_ARCH=${HOST_ARCH:-amd64}
|
||||
ARCH=${ARCH:-amd64}
|
||||
|
||||
declare -A qemuarch=( ["amd64"]="x86_64" ["arm"]="arm" ["arm64"]="aarch64" )
|
||||
declare -A ttycons=( ["amd64"]="ttyS0" ["arm"]="ttyAMA0" ["arm64"]="ttyAMA0" )
|
||||
declare -A machine=( ["amd64"]="" ["arm"]="-M virt" ["arm64"]="-M virt" )
|
||||
declare -A cpu=( ["amd64"]="" ["arm"]="-cpu cortex-a8" ["arm64"]="-cpu cortex-a57" )
|
||||
declare -A network=(
|
||||
["amd64"]="-net nic,vlan=0,model=virtio -net user,vlan=0,hostfwd=tcp::2222-:22,hostname=rancher-dev"
|
||||
["arm"]="-netdev user,id=unet,hostfwd=tcp::2222-:22,hostname=rancher-dev -device virtio-net-device,netdev=unet"
|
||||
)
|
||||
network["arm64"]=${network["arm"]}
|
||||
|
||||
hd_amd64() {
|
||||
echo "-drive if=virtio,file=$1"
|
||||
}
|
||||
hd_arm() {
|
||||
echo "-drive if=none,id=hd0,file=$1 -device virtio-blk-device,drive=hd0"
|
||||
}
|
||||
declare -A hd=( ["amd64"]="hd_amd64" ["arm"]="hd_arm" ["arm64"]="hd_arm" )
|
||||
|
||||
cd_amd64() {
|
||||
echo "-hdc $1"
|
||||
}
|
||||
cd_arm() {
|
||||
echo "-drive if=none,id=cd0,file=$1 -device virtio-blk-device,drive=cd0"
|
||||
}
|
||||
declare -A cd=( ["amd64"]="cd_amd64" ["arm"]="cd_arm" ["arm64"]="cd_arm" )
|
||||
|
||||
BUILD=build
|
||||
BASE=$(pwd)
|
||||
UNAME=$(uname)
|
||||
|
||||
KERNEL=${BASE}/dist/artifacts/vmlinuz
|
||||
INITRD_SRC=${BASE}/build/initrd
|
||||
INITRD=${BASE}/build/initrd.tmp
|
||||
|
||||
QEMU=1
|
||||
FORMAT=1
|
||||
RM_USR=1
|
||||
REBUILD=1
|
||||
source ./scripts/run-common
|
||||
|
||||
while [ "$#" -gt 0 ]; do
|
||||
case $1 in
|
||||
@@ -93,6 +53,10 @@ while [ "$#" -gt 0 ]; do
|
||||
--fresh)
|
||||
FRESH=1
|
||||
;;
|
||||
--installed)
|
||||
./scripts/create-installed
|
||||
INSTALLED=1
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
@@ -100,9 +64,6 @@ while [ "$#" -gt 0 ]; do
|
||||
shift 1
|
||||
done
|
||||
|
||||
QEMUARCH=${qemuarch["${ARCH}"]}
|
||||
TTYCONS=${ttycons["${ARCH}"]}
|
||||
|
||||
if [ "$QEMU" == "1" ] && ! which qemu-system-${QEMUARCH}; then
|
||||
QEMU=0
|
||||
fi
|
||||
@@ -120,9 +81,9 @@ if [ "$REBUILD" == "1" ] || [ ! -e ${INITRD} ]; then
|
||||
popd >/dev/null
|
||||
fi
|
||||
|
||||
KERNEL_ARGS="quiet rancher.password=rancher console=${TTYCONS} rancher.autologin=${TTYCONS} ${QEMU_APPEND}"
|
||||
KERNEL_ARGS="${DEFAULT_KERNEL_ARGS} ${QEMU_APPEND}"
|
||||
if [ "$FORMAT" == "1" ]; then
|
||||
KERNEL_ARGS="${KERNEL_ARGS} rancher.state.dev=LABEL=RANCHER_STATE rancher.state.formatzero=true rancher.state.autoformat=[/dev/sda,/dev/vda]"
|
||||
KERNEL_ARGS="${KERNEL_ARGS} rancher.state.dev=LABEL=RANCHER_STATE rancher.state.autoformat=[/dev/sda,/dev/vda]"
|
||||
fi
|
||||
if [ "$RM_USR" == "1" ]; then
|
||||
KERNEL_ARGS="${KERNEL_ARGS} rancher.rm_usr"
|
||||
@@ -168,29 +129,51 @@ if [ "$QEMU" == "1" ]; then
|
||||
KVM_ENABLE="-enable-kvm"
|
||||
CPU="-cpu host"
|
||||
fi
|
||||
set -x
|
||||
HOME=${HOME:-/}
|
||||
exec qemu-system-${QEMUARCH} -serial stdio \
|
||||
-rtc base=utc,clock=host \
|
||||
${KVM_ENABLE} \
|
||||
${CPU} \
|
||||
${machine["$ARCH"]} \
|
||||
-kernel ${KERNEL} \
|
||||
-initrd ${INITRD} \
|
||||
-m 2048 \
|
||||
${network["$ARCH"]} \
|
||||
$(eval "${hd["$ARCH"]} ${HD}") \
|
||||
${SECOND_DRIVE_ENABLE} \
|
||||
-smp 1 \
|
||||
-append "${KERNEL_ARGS}" \
|
||||
-nographic \
|
||||
-display none \
|
||||
-fsdev local,security_model=passthrough,readonly,id=fsdev0,path=${CCROOT} \
|
||||
-device virtio-9p-pci,id=fs0,fsdev=fsdev0,mount_tag=config-2 \
|
||||
-fsdev local,security_model=none,id=fsdev1,path=${HOME} \
|
||||
-device virtio-9p-pci,id=fs1,fsdev=fsdev1,mount_tag=home \
|
||||
${QEMU_ARGS} \
|
||||
"${@}"
|
||||
if [ "$INSTALLED" == "1" ]; then
|
||||
set -x
|
||||
exec qemu-system-${QEMUARCH} -serial stdio \
|
||||
-rtc base=utc,clock=host \
|
||||
${KVM_ENABLE} \
|
||||
${CPU} \
|
||||
${machine["$ARCH"]} \
|
||||
-m 2048 \
|
||||
${network["$ARCH"]} \
|
||||
$(eval "${hd["$ARCH"]} ${HD}") \
|
||||
${SECOND_DRIVE_ENABLE} \
|
||||
-smp 1 \
|
||||
-nographic \
|
||||
-display none \
|
||||
-fsdev local,security_model=passthrough,readonly,id=fsdev0,path=${CCROOT} \
|
||||
-device virtio-9p-pci,id=fs0,fsdev=fsdev0,mount_tag=config-2 \
|
||||
-fsdev local,security_model=none,id=fsdev1,path=${HOME} \
|
||||
-device virtio-9p-pci,id=fs1,fsdev=fsdev1,mount_tag=home \
|
||||
${QEMU_ARGS} \
|
||||
"${@}"
|
||||
else
|
||||
set -x
|
||||
exec qemu-system-${QEMUARCH} -serial stdio \
|
||||
-rtc base=utc,clock=host \
|
||||
${KVM_ENABLE} \
|
||||
${CPU} \
|
||||
${machine["$ARCH"]} \
|
||||
-kernel ${KERNEL} \
|
||||
-initrd ${INITRD} \
|
||||
-m 2048 \
|
||||
${network["$ARCH"]} \
|
||||
$(eval "${hd["$ARCH"]} ${HD}") \
|
||||
${SECOND_DRIVE_ENABLE} \
|
||||
-smp 1 \
|
||||
-append "${KERNEL_ARGS}" \
|
||||
-nographic \
|
||||
-display none \
|
||||
-fsdev local,security_model=passthrough,readonly,id=fsdev0,path=${CCROOT} \
|
||||
-device virtio-9p-pci,id=fs0,fsdev=fsdev0,mount_tag=config-2 \
|
||||
-fsdev local,security_model=none,id=fsdev1,path=${HOME} \
|
||||
-device virtio-9p-pci,id=fs1,fsdev=fsdev1,mount_tag=home \
|
||||
${QEMU_ARGS} \
|
||||
"${@}"
|
||||
fi
|
||||
|
||||
elif [ "$QIND" == "1" ]; then
|
||||
|
||||
|
||||
49
scripts/run-common
Executable file
49
scripts/run-common
Executable file
@@ -0,0 +1,49 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
HOST_ARCH=${HOST_ARCH:-amd64}
|
||||
ARCH=${ARCH:-amd64}
|
||||
|
||||
declare -A qemuarch=( ["amd64"]="x86_64" ["arm"]="arm" ["arm64"]="aarch64" )
|
||||
declare -A ttycons=( ["amd64"]="ttyS0" ["arm"]="ttyAMA0" ["arm64"]="ttyAMA0" )
|
||||
declare -A machine=( ["amd64"]="" ["arm"]="-M virt" ["arm64"]="-M virt" )
|
||||
declare -A cpu=( ["amd64"]="" ["arm"]="-cpu cortex-a8" ["arm64"]="-cpu cortex-a57" )
|
||||
declare -A network=(
|
||||
["amd64"]="-net nic,vlan=0,model=virtio -net user,vlan=0,hostfwd=tcp::2222-:22,hostname=rancher-dev"
|
||||
["arm"]="-netdev user,id=unet,hostfwd=tcp::2222-:22,hostname=rancher-dev -device virtio-net-device,netdev=unet"
|
||||
)
|
||||
network["arm64"]=${network["arm"]}
|
||||
|
||||
hd_amd64() {
|
||||
echo "-drive if=virtio,file=$1"
|
||||
}
|
||||
hd_arm() {
|
||||
echo "-drive if=none,id=hd0,file=$1 -device virtio-blk-device,drive=hd0"
|
||||
}
|
||||
declare -A hd=( ["amd64"]="hd_amd64" ["arm"]="hd_arm" ["arm64"]="hd_arm" )
|
||||
|
||||
cd_amd64() {
|
||||
echo "-hdc $1"
|
||||
}
|
||||
cd_arm() {
|
||||
echo "-drive if=none,id=cd0,file=$1 -device virtio-blk-device,drive=cd0"
|
||||
}
|
||||
declare -A cd=( ["amd64"]="cd_amd64" ["arm"]="cd_arm" ["arm64"]="cd_arm" )
|
||||
|
||||
BUILD=build
|
||||
BASE=$(pwd)
|
||||
UNAME=$(uname)
|
||||
|
||||
KERNEL=${BASE}/dist/artifacts/vmlinuz
|
||||
INITRD_SRC=${BASE}/build/initrd
|
||||
INITRD=${BASE}/build/initrd.tmp
|
||||
|
||||
QEMU=1
|
||||
FORMAT=1
|
||||
RM_USR=1
|
||||
REBUILD=1
|
||||
|
||||
QEMUARCH=${qemuarch["${ARCH}"]}
|
||||
TTYCONS=${ttycons["${ARCH}"]}
|
||||
|
||||
DEFAULT_KERNEL_ARGS="quiet rancher.password=rancher console=${TTYCONS} rancher.autologin=${TTYCONS}"
|
||||
@@ -9,12 +9,12 @@ PACKAGES=". $(find -name '*.go' | xargs -I{} dirname {} | cut -f2 -d/ | sort -u
|
||||
|
||||
echo Running: go vet
|
||||
go vet ${PACKAGES}
|
||||
echo Running: golint
|
||||
for i in ${PACKAGES}; do
|
||||
if [ -n "$(golint $i | grep -v 'should have comment.*or be unexported' | tee /dev/stderr)" ]; then
|
||||
failed=true
|
||||
fi
|
||||
done
|
||||
#echo Running: golint
|
||||
#for i in ${PACKAGES}; do
|
||||
# if [ -n "$(golint $i | grep -v 'should have comment.*or be unexported' | tee /dev/stderr)" ]; then
|
||||
# failed=true
|
||||
# fi
|
||||
#done
|
||||
test -z "$failed"
|
||||
echo Running: go fmt
|
||||
test -z "$(go fmt ${PACKAGES} | tee /dev/stderr)"
|
||||
|
||||
@@ -11,7 +11,5 @@ rancher:
|
||||
address: 10.10.2.17/24
|
||||
gateway: 10.10.2.2
|
||||
mtu: 1500
|
||||
docker:
|
||||
args: [daemon, --log-opt, max-file=2, --log-opt, max-size=25m, -s, overlay, -G, docker, -H, 'unix:///var/run/docker.sock', --userland-proxy=false]
|
||||
ssh_authorized_keys:
|
||||
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC85w9stZyiLQp/DkVO6fqwiShYcj1ClKdtCqgHtf+PLpJkFReSFu8y21y+ev09gsSMRRrjF7yt0pUHV6zncQhVeqsZtgc5WbELY2DOYUGmRn/CCvPbXovoBrQjSorqlBmpuPwsStYLr92Xn+VVsMNSUIegHY22DphGbDKG85vrKB8HxUxGIDxFBds/uE8FhSy+xsoyT/jUZDK6pgq2HnGl6D81ViIlKecpOpWlW3B+fea99ADNyZNVvDzbHE5pcI3VRw8u59WmpWOUgT6qacNVACl8GqpBvQk8sw7O/X9DSZHCKafeD9G5k+GYbAUz92fKWrx/lOXfUXPS3+c8dRIF
|
||||
|
||||
@@ -5,7 +5,23 @@ write_files:
|
||||
owner: root
|
||||
content: |
|
||||
#!/bin/bash
|
||||
touch /home/rancher/test
|
||||
touch /home/rancher/test1
|
||||
- path: /opt/rancher/bin/start.sh
|
||||
permissions: "0755"
|
||||
owner: root
|
||||
content: |
|
||||
touch /home/rancher/test3
|
||||
- path: /etc/rc.local
|
||||
permissions: "0755"
|
||||
owner: root
|
||||
content: |
|
||||
touch /home/rancher/test4
|
||||
- path: /var/lib/rancher/conf/cloud-config-script
|
||||
permissions: "0755"
|
||||
owner: root
|
||||
content: |
|
||||
#!/bin/bash
|
||||
touch /home/rancher/test5
|
||||
runcmd:
|
||||
- []
|
||||
- [ test ]
|
||||
|
||||
@@ -56,7 +56,23 @@ func (s *QemuSuite) RunQemu(additionalArgs ...string) error {
|
||||
}
|
||||
runArgs = append(runArgs, additionalArgs...)
|
||||
|
||||
s.qemuCmd = exec.Command(s.runCommand, runArgs...)
|
||||
return s.runQemu(runArgs...)
|
||||
}
|
||||
|
||||
func (s *QemuSuite) RunQemuInstalled(additionalArgs ...string) error {
|
||||
runArgs := []string{
|
||||
"--qemu",
|
||||
"--no-rebuild",
|
||||
"--no-rm-usr",
|
||||
"--installed",
|
||||
}
|
||||
runArgs = append(runArgs, additionalArgs...)
|
||||
|
||||
return s.runQemu(runArgs...)
|
||||
}
|
||||
|
||||
func (s *QemuSuite) runQemu(args ...string) error {
|
||||
s.qemuCmd = exec.Command(s.runCommand, args...)
|
||||
s.qemuCmd.Stdout = os.Stdout
|
||||
s.qemuCmd.Stderr = os.Stderr
|
||||
if err := s.qemuCmd.Start(); err != nil {
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
. "gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
func (s *QemuSuite) TestInstall(c *C) {
|
||||
err := s.RunQemu("--no-format")
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
s.LoadInstallerImage(c)
|
||||
|
||||
s.CheckCall(c, fmt.Sprintf(`
|
||||
sudo mkfs.ext4 /dev/vda
|
||||
sudo ros install -f --no-reboot -d /dev/vda -i rancher/os:%s%s`, Version, Suffix))
|
||||
}
|
||||
@@ -8,7 +8,6 @@ func (s *QemuSuite) TestMisc(c *C) {
|
||||
|
||||
s.CheckCall(c, "sudo ros env printenv FLANNEL_NETWORK | grep '10.244.0.0/16'")
|
||||
|
||||
s.CheckCall(c, "ps -ef | grep 'daemon --log-opt max-file=2 --log-opt max-size=25m -s overlay -G docker -H unix:///var/run/docker.sock --userland-proxy=false'")
|
||||
s.CheckCall(c, "ps -ef | grep 'dhcpcd -M'")
|
||||
|
||||
s.CheckCall(c, `
|
||||
|
||||
@@ -8,10 +8,10 @@ func (s *QemuSuite) TestMounts(c *C) {
|
||||
|
||||
s.CheckCall(c, "cat /home/rancher/test | grep test")
|
||||
|
||||
s.CheckCall(c, "mkdir -p /home/rancher/a /home/rancher/b /home/rancher/c")
|
||||
s.CheckCall(c, "sudo mkfs.ext4 /dev/vdb")
|
||||
s.CheckCall(c, "sudo cloud-init-execute")
|
||||
s.CheckCall(c, "mount | grep /home/rancher/a")
|
||||
s.CheckCall(c, "mount | grep /home/rancher/b")
|
||||
s.CheckCall(c, "mount | grep /home/rancher/c")
|
||||
s.CheckCall(c, "mkdir -p /home/rancher/a /home/rancher/b /home/rancher/c")
|
||||
s.CheckCall(c, "sudo mkfs.ext4 /dev/vdb")
|
||||
s.CheckCall(c, "sudo cloud-init-execute")
|
||||
s.CheckCall(c, "mount | grep /home/rancher/a")
|
||||
s.CheckCall(c, "mount | grep /home/rancher/b")
|
||||
s.CheckCall(c, "mount | grep /home/rancher/c")
|
||||
}
|
||||
|
||||
10
tests/nonexistent_state_test.go
Normal file
10
tests/nonexistent_state_test.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package integration
|
||||
|
||||
import . "gopkg.in/check.v1"
|
||||
|
||||
func (s *QemuSuite) TestNonexistentState(c *C) {
|
||||
err := s.RunQemu("--no-format", "--append", "rancher.state.dev=LABEL=NONEXISTENT")
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
s.CheckCall(c, "sudo ros config get rancher.state.dev | grep LABEL=NONEXISTENT")
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package integration
|
||||
|
||||
import . "gopkg.in/check.v1"
|
||||
|
||||
func (s *QemuSuite) TestRuncmd(c *C) {
|
||||
err := s.RunQemu("--cloud-config", "./tests/assets/test_26/cloud-config.yml")
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
s.CheckCall(c, "ls /home/rancher | grep test")
|
||||
s.CheckCall(c, "ls /home/rancher | grep test2")
|
||||
}
|
||||
16
tests/start_commands_test.go
Normal file
16
tests/start_commands_test.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
. "gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
func (s *QemuSuite) TestStartCommands(c *C) {
|
||||
err := s.RunQemu("--cloud-config", "./tests/assets/test_26/cloud-config.yml")
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
for i := 1; i < 6; i++ {
|
||||
s.CheckCall(c, fmt.Sprintf("ls /home/rancher | grep test%d", i))
|
||||
}
|
||||
}
|
||||
26
tests/upgrade_test.go
Normal file
26
tests/upgrade_test.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
. "gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
func (s *QemuSuite) TestUpgrade(c *C) {
|
||||
err := s.RunQemuInstalled()
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
s.CheckCall(c, `
|
||||
set -ex
|
||||
sudo ros os upgrade -i rancher/os:v0.5.0 --force --no-reboot`)
|
||||
|
||||
s.Reboot(c)
|
||||
|
||||
s.CheckCall(c, "sudo ros -v | grep v0.5.0")
|
||||
s.LoadInstallerImage(c)
|
||||
s.CheckCall(c, fmt.Sprintf("sudo ros os upgrade -i rancher/os:%s%s --force --no-reboot", Version, Suffix))
|
||||
|
||||
s.Reboot(c)
|
||||
|
||||
s.CheckCall(c, fmt.Sprintf("sudo ros -v | grep %s", Version))
|
||||
}
|
||||
@@ -33,7 +33,7 @@ github.com/mattn/go-shellwords v1.0.0
|
||||
github.com/opencontainers/runc edc34c4a8c1e261b5ce926ff557ecde1aff19ce3 https://github.com/ibuildthecloud/runc.git
|
||||
github.com/opencontainers/runtime-spec f955d90e70a98ddfb886bd930ffd076da9b67998
|
||||
github.com/opencontainers/specs f955d90e70a98ddfb886bd930ffd076da9b67998
|
||||
github.com/packethost/packngo 92012705236896736875186c9e49557897c6af90 https://github.com/ibuildthecloud/packngo.git
|
||||
github.com/packethost/packngo 7b3a781a3c8c45b0e55390fa3c4d24981402b99f https://github.com/joshwget/packngo.git
|
||||
github.com/pkg/errors d62207b3dc916c342cd6a7180fa861d898cf42ee
|
||||
github.com/pmezard/go-difflib d8ed2627bdf02c080bf22230dbb337003b7aba2d
|
||||
github.com/rancher/cniglue b56bd68e5df113ad3fcc59c58034c22afaede877
|
||||
@@ -55,3 +55,4 @@ golang.org/x/net 991d3e32f76f19ee6d9caadb3a22eae8d23315f7 https://github.com/gol
|
||||
golang.org/x/sys eb2c74142fd19a79b3f237334c7384d5167b1b46 https://github.com/golang/sys.git
|
||||
google.golang.org/grpc ab0be5212fb225475f2087566eded7da5d727960 https://github.com/grpc/grpc-go.git
|
||||
gopkg.in/fsnotify.v1 v1.2.0
|
||||
github.com/fatih/structs dc3312cb1a4513a366c4c9e622ad55c32df12ed3
|
||||
|
||||
23
vendor/github.com/fatih/structs/.gitignore
generated
vendored
Normal file
23
vendor/github.com/fatih/structs/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
11
vendor/github.com/fatih/structs/.travis.yml
generated
vendored
Normal file
11
vendor/github.com/fatih/structs/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
language: go
|
||||
go:
|
||||
- 1.6
|
||||
- tip
|
||||
sudo: false
|
||||
before_install:
|
||||
- go get github.com/axw/gocov/gocov
|
||||
- go get github.com/mattn/goveralls
|
||||
- if ! go get github.com/golang/tools/cmd/cover; then go get golang.org/x/tools/cmd/cover; fi
|
||||
script:
|
||||
- $HOME/gopath/bin/goveralls -service=travis-ci
|
||||
21
vendor/github.com/fatih/structs/LICENSE
generated
vendored
Normal file
21
vendor/github.com/fatih/structs/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Fatih Arslan
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
163
vendor/github.com/fatih/structs/README.md
generated
vendored
Normal file
163
vendor/github.com/fatih/structs/README.md
generated
vendored
Normal file
@@ -0,0 +1,163 @@
|
||||
# Structs [](http://godoc.org/github.com/fatih/structs) [](https://travis-ci.org/fatih/structs) [](https://coveralls.io/r/fatih/structs)
|
||||
|
||||
Structs contains various utilities to work with Go (Golang) structs. It was
|
||||
initially used by me to convert a struct into a `map[string]interface{}`. With
|
||||
time I've added other utilities for structs. It's basically a high level
|
||||
package based on primitives from the reflect package. Feel free to add new
|
||||
functions or improve the existing code.
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
go get github.com/fatih/structs
|
||||
```
|
||||
|
||||
## Usage and Examples
|
||||
|
||||
Just like the standard lib `strings`, `bytes` and co packages, `structs` has
|
||||
many global functions to manipulate or organize your struct data. Lets define
|
||||
and declare a struct:
|
||||
|
||||
```go
|
||||
type Server struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
ID int
|
||||
Enabled bool
|
||||
users []string // not exported
|
||||
http.Server // embedded
|
||||
}
|
||||
|
||||
server := &Server{
|
||||
Name: "gopher",
|
||||
ID: 123456,
|
||||
Enabled: true,
|
||||
}
|
||||
```
|
||||
|
||||
```go
|
||||
// Convert a struct to a map[string]interface{}
|
||||
// => {"Name":"gopher", "ID":123456, "Enabled":true}
|
||||
m := structs.Map(server)
|
||||
|
||||
// Convert the values of a struct to a []interface{}
|
||||
// => ["gopher", 123456, true]
|
||||
v := structs.Values(server)
|
||||
|
||||
// Convert the names of a struct to a []string
|
||||
// (see "Names methods" for more info about fields)
|
||||
n := structs.Names(server)
|
||||
|
||||
// Convert the values of a struct to a []*Field
|
||||
// (see "Field methods" for more info about fields)
|
||||
f := structs.Fields(server)
|
||||
|
||||
// Return the struct name => "Server"
|
||||
n := structs.Name(server)
|
||||
|
||||
// Check if any field of a struct is initialized or not.
|
||||
h := structs.HasZero(server)
|
||||
|
||||
// Check if all fields of a struct is initialized or not.
|
||||
z := structs.IsZero(server)
|
||||
|
||||
// Check if server is a struct or a pointer to struct
|
||||
i := structs.IsStruct(server)
|
||||
```
|
||||
|
||||
### Struct methods
|
||||
|
||||
The structs functions can be also used as independent methods by creating a new
|
||||
`*structs.Struct`. This is handy if you want to have more control over the
|
||||
structs (such as retrieving a single Field).
|
||||
|
||||
```go
|
||||
// Create a new struct type:
|
||||
s := structs.New(server)
|
||||
|
||||
m := s.Map() // Get a map[string]interface{}
|
||||
v := s.Values() // Get a []interface{}
|
||||
f := s.Fields() // Get a []*Field
|
||||
n := s.Names() // Get a []string
|
||||
f := s.Field(name) // Get a *Field based on the given field name
|
||||
f, ok := s.FieldOk(name) // Get a *Field based on the given field name
|
||||
n := s.Name() // Get the struct name
|
||||
h := s.HasZero() // Check if any field is initialized
|
||||
z := s.IsZero() // Check if all fields are initialized
|
||||
```
|
||||
|
||||
### Field methods
|
||||
|
||||
We can easily examine a single Field for more detail. Below you can see how we
|
||||
get and interact with various field methods:
|
||||
|
||||
|
||||
```go
|
||||
s := structs.New(server)
|
||||
|
||||
// Get the Field struct for the "Name" field
|
||||
name := s.Field("Name")
|
||||
|
||||
// Get the underlying value, value => "gopher"
|
||||
value := name.Value().(string)
|
||||
|
||||
// Set the field's value
|
||||
name.Set("another gopher")
|
||||
|
||||
// Get the field's kind, kind => "string"
|
||||
name.Kind()
|
||||
|
||||
// Check if the field is exported or not
|
||||
if name.IsExported() {
|
||||
fmt.Println("Name field is exported")
|
||||
}
|
||||
|
||||
// Check if the value is a zero value, such as "" for string, 0 for int
|
||||
if !name.IsZero() {
|
||||
fmt.Println("Name is initialized")
|
||||
}
|
||||
|
||||
// Check if the field is an anonymous (embedded) field
|
||||
if !name.IsEmbedded() {
|
||||
fmt.Println("Name is not an embedded field")
|
||||
}
|
||||
|
||||
// Get the Field's tag value for tag name "json", tag value => "name,omitempty"
|
||||
tagValue := name.Tag("json")
|
||||
```
|
||||
|
||||
Nested structs are supported too:
|
||||
|
||||
```go
|
||||
addrField := s.Field("Server").Field("Addr")
|
||||
|
||||
// Get the value for addr
|
||||
a := addrField.Value().(string)
|
||||
|
||||
// Or get all fields
|
||||
httpServer := s.Field("Server").Fields()
|
||||
```
|
||||
|
||||
We can also get a slice of Fields from the Struct type to iterate over all
|
||||
fields. This is handy if you wish to examine all fields:
|
||||
|
||||
```go
|
||||
s := structs.New(server)
|
||||
|
||||
for _, f := range s.Fields() {
|
||||
fmt.Printf("field name: %+v\n", f.Name())
|
||||
|
||||
if f.IsExported() {
|
||||
fmt.Printf("value : %+v\n", f.Value())
|
||||
fmt.Printf("is zero : %+v\n", f.IsZero())
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Credits
|
||||
|
||||
* [Fatih Arslan](https://github.com/fatih)
|
||||
* [Cihangir Savas](https://github.com/cihangir)
|
||||
|
||||
## License
|
||||
|
||||
The MIT License (MIT) - see LICENSE.md for more details
|
||||
141
vendor/github.com/fatih/structs/field.go
generated
vendored
Normal file
141
vendor/github.com/fatih/structs/field.go
generated
vendored
Normal file
@@ -0,0 +1,141 @@
|
||||
package structs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
var (
|
||||
errNotExported = errors.New("field is not exported")
|
||||
errNotSettable = errors.New("field is not settable")
|
||||
)
|
||||
|
||||
// Field represents a single struct field that encapsulates high level
|
||||
// functions around the field.
|
||||
type Field struct {
|
||||
value reflect.Value
|
||||
field reflect.StructField
|
||||
defaultTag string
|
||||
}
|
||||
|
||||
// Tag returns the value associated with key in the tag string. If there is no
|
||||
// such key in the tag, Tag returns the empty string.
|
||||
func (f *Field) Tag(key string) string {
|
||||
return f.field.Tag.Get(key)
|
||||
}
|
||||
|
||||
// Value returns the underlying value of the field. It panics if the field
|
||||
// is not exported.
|
||||
func (f *Field) Value() interface{} {
|
||||
return f.value.Interface()
|
||||
}
|
||||
|
||||
// IsEmbedded returns true if the given field is an anonymous field (embedded)
|
||||
func (f *Field) IsEmbedded() bool {
|
||||
return f.field.Anonymous
|
||||
}
|
||||
|
||||
// IsExported returns true if the given field is exported.
|
||||
func (f *Field) IsExported() bool {
|
||||
return f.field.PkgPath == ""
|
||||
}
|
||||
|
||||
// IsZero returns true if the given field is not initialized (has a zero value).
|
||||
// It panics if the field is not exported.
|
||||
func (f *Field) IsZero() bool {
|
||||
zero := reflect.Zero(f.value.Type()).Interface()
|
||||
current := f.Value()
|
||||
|
||||
return reflect.DeepEqual(current, zero)
|
||||
}
|
||||
|
||||
// Name returns the name of the given field
|
||||
func (f *Field) Name() string {
|
||||
return f.field.Name
|
||||
}
|
||||
|
||||
// Kind returns the fields kind, such as "string", "map", "bool", etc ..
|
||||
func (f *Field) Kind() reflect.Kind {
|
||||
return f.value.Kind()
|
||||
}
|
||||
|
||||
// Set sets the field to given value v. It returns an error if the field is not
|
||||
// settable (not addressable or not exported) or if the given value's type
|
||||
// doesn't match the fields type.
|
||||
func (f *Field) Set(val interface{}) error {
|
||||
// we can't set unexported fields, so be sure this field is exported
|
||||
if !f.IsExported() {
|
||||
return errNotExported
|
||||
}
|
||||
|
||||
// do we get here? not sure...
|
||||
if !f.value.CanSet() {
|
||||
return errNotSettable
|
||||
}
|
||||
|
||||
given := reflect.ValueOf(val)
|
||||
|
||||
if f.value.Kind() != given.Kind() {
|
||||
return fmt.Errorf("wrong kind. got: %s want: %s", given.Kind(), f.value.Kind())
|
||||
}
|
||||
|
||||
f.value.Set(given)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Zero sets the field to its zero value. It returns an error if the field is not
|
||||
// settable (not addressable or not exported).
|
||||
func (f *Field) Zero() error {
|
||||
zero := reflect.Zero(f.value.Type()).Interface()
|
||||
return f.Set(zero)
|
||||
}
|
||||
|
||||
// Fields returns a slice of Fields. This is particular handy to get the fields
|
||||
// of a nested struct . A struct tag with the content of "-" ignores the
|
||||
// checking of that particular field. Example:
|
||||
//
|
||||
// // Field is ignored by this package.
|
||||
// Field *http.Request `structs:"-"`
|
||||
//
|
||||
// It panics if field is not exported or if field's kind is not struct
|
||||
func (f *Field) Fields() []*Field {
|
||||
return getFields(f.value, f.defaultTag)
|
||||
}
|
||||
|
||||
// Field returns the field from a nested struct. It panics if the nested struct
|
||||
// is not exported or if the field was not found.
|
||||
func (f *Field) Field(name string) *Field {
|
||||
field, ok := f.FieldOk(name)
|
||||
if !ok {
|
||||
panic("field not found")
|
||||
}
|
||||
|
||||
return field
|
||||
}
|
||||
|
||||
// FieldOk returns the field from a nested struct. The boolean returns whether
|
||||
// the field was found (true) or not (false).
|
||||
func (f *Field) FieldOk(name string) (*Field, bool) {
|
||||
value := &f.value
|
||||
// value must be settable so we need to make sure it holds the address of the
|
||||
// variable and not a copy, so we can pass the pointer to strctVal instead of a
|
||||
// copy (which is not assigned to any variable, hence not settable).
|
||||
// see "https://blog.golang.org/laws-of-reflection#TOC_8."
|
||||
if f.value.Kind() != reflect.Ptr {
|
||||
a := f.value.Addr()
|
||||
value = &a
|
||||
}
|
||||
v := strctVal(value.Interface())
|
||||
t := v.Type()
|
||||
|
||||
field, ok := t.FieldByName(name)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return &Field{
|
||||
field: field,
|
||||
value: v.FieldByName(name),
|
||||
}, true
|
||||
}
|
||||
579
vendor/github.com/fatih/structs/structs.go
generated
vendored
Normal file
579
vendor/github.com/fatih/structs/structs.go
generated
vendored
Normal file
@@ -0,0 +1,579 @@
|
||||
// Package structs contains various utilities functions to work with structs.
|
||||
package structs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"reflect"
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultTagName is the default tag name for struct fields which provides
|
||||
// a more granular to tweak certain structs. Lookup the necessary functions
|
||||
// for more info.
|
||||
DefaultTagName = "structs" // struct's field default tag name
|
||||
)
|
||||
|
||||
// Struct encapsulates a struct type to provide several high level functions
|
||||
// around the struct.
|
||||
type Struct struct {
|
||||
raw interface{}
|
||||
value reflect.Value
|
||||
TagName string
|
||||
}
|
||||
|
||||
// New returns a new *Struct with the struct s. It panics if the s's kind is
|
||||
// not struct.
|
||||
func New(s interface{}) *Struct {
|
||||
return &Struct{
|
||||
raw: s,
|
||||
value: strctVal(s),
|
||||
TagName: DefaultTagName,
|
||||
}
|
||||
}
|
||||
|
||||
// Map converts the given struct to a map[string]interface{}, where the keys
|
||||
// of the map are the field names and the values of the map the associated
|
||||
// values of the fields. The default key string is the struct field name but
|
||||
// can be changed in the struct field's tag value. The "structs" key in the
|
||||
// struct's field tag value is the key name. Example:
|
||||
//
|
||||
// // Field appears in map as key "myName".
|
||||
// Name string `structs:"myName"`
|
||||
//
|
||||
// A tag value with the content of "-" ignores that particular field. Example:
|
||||
//
|
||||
// // Field is ignored by this package.
|
||||
// Field bool `structs:"-"`
|
||||
//
|
||||
// A tag value with the content of "string" uses the stringer to get the value. Example:
|
||||
//
|
||||
// // The value will be output of Animal's String() func.
|
||||
// // Map will panic if Animal does not implement String().
|
||||
// Field *Animal `structs:"field,string"`
|
||||
//
|
||||
// A tag value with the option of "flatten" used in a struct field is to flatten its fields
|
||||
// in the output map. Example:
|
||||
//
|
||||
// // The FieldStruct's fields will be flattened into the output map.
|
||||
// FieldStruct time.Time `structs:",flatten"`
|
||||
//
|
||||
// A tag value with the option of "omitnested" stops iterating further if the type
|
||||
// is a struct. Example:
|
||||
//
|
||||
// // Field is not processed further by this package.
|
||||
// Field time.Time `structs:"myName,omitnested"`
|
||||
// Field *http.Request `structs:",omitnested"`
|
||||
//
|
||||
// A tag value with the option of "omitempty" ignores that particular field if
|
||||
// the field value is empty. Example:
|
||||
//
|
||||
// // Field appears in map as key "myName", but the field is
|
||||
// // skipped if empty.
|
||||
// Field string `structs:"myName,omitempty"`
|
||||
//
|
||||
// // Field appears in map as key "Field" (the default), but
|
||||
// // the field is skipped if empty.
|
||||
// Field string `structs:",omitempty"`
|
||||
//
|
||||
// Note that only exported fields of a struct can be accessed, non exported
|
||||
// fields will be neglected.
|
||||
func (s *Struct) Map() map[string]interface{} {
|
||||
out := make(map[string]interface{})
|
||||
s.FillMap(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// FillMap is the same as Map. Instead of returning the output, it fills the
|
||||
// given map.
|
||||
func (s *Struct) FillMap(out map[string]interface{}) {
|
||||
if out == nil {
|
||||
return
|
||||
}
|
||||
|
||||
fields := s.structFields()
|
||||
|
||||
for _, field := range fields {
|
||||
name := field.Name
|
||||
val := s.value.FieldByName(name)
|
||||
isSubStruct := false
|
||||
var finalVal interface{}
|
||||
|
||||
tagName, tagOpts := parseTag(field.Tag.Get(s.TagName))
|
||||
if tagName != "" {
|
||||
name = tagName
|
||||
}
|
||||
|
||||
// if the value is a zero value and the field is marked as omitempty do
|
||||
// not include
|
||||
if tagOpts.Has("omitempty") {
|
||||
zero := reflect.Zero(val.Type()).Interface()
|
||||
current := val.Interface()
|
||||
|
||||
if reflect.DeepEqual(current, zero) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if !tagOpts.Has("omitnested") {
|
||||
finalVal = s.nested(val)
|
||||
|
||||
v := reflect.ValueOf(val.Interface())
|
||||
if v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
|
||||
switch v.Kind() {
|
||||
case reflect.Map, reflect.Struct:
|
||||
isSubStruct = true
|
||||
}
|
||||
} else {
|
||||
finalVal = val.Interface()
|
||||
}
|
||||
|
||||
if tagOpts.Has("string") {
|
||||
s, ok := val.Interface().(fmt.Stringer)
|
||||
if ok {
|
||||
out[name] = s.String()
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if isSubStruct && (tagOpts.Has("flatten")) {
|
||||
for k := range finalVal.(map[string]interface{}) {
|
||||
out[k] = finalVal.(map[string]interface{})[k]
|
||||
}
|
||||
} else {
|
||||
out[name] = finalVal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Values converts the given s struct's field values to a []interface{}. A
|
||||
// struct tag with the content of "-" ignores the that particular field.
|
||||
// Example:
|
||||
//
|
||||
// // Field is ignored by this package.
|
||||
// Field int `structs:"-"`
|
||||
//
|
||||
// A value with the option of "omitnested" stops iterating further if the type
|
||||
// is a struct. Example:
|
||||
//
|
||||
// // Fields is not processed further by this package.
|
||||
// Field time.Time `structs:",omitnested"`
|
||||
// Field *http.Request `structs:",omitnested"`
|
||||
//
|
||||
// A tag value with the option of "omitempty" ignores that particular field and
|
||||
// is not added to the values if the field value is empty. Example:
|
||||
//
|
||||
// // Field is skipped if empty
|
||||
// Field string `structs:",omitempty"`
|
||||
//
|
||||
// Note that only exported fields of a struct can be accessed, non exported
|
||||
// fields will be neglected.
|
||||
func (s *Struct) Values() []interface{} {
|
||||
fields := s.structFields()
|
||||
|
||||
var t []interface{}
|
||||
|
||||
for _, field := range fields {
|
||||
val := s.value.FieldByName(field.Name)
|
||||
|
||||
_, tagOpts := parseTag(field.Tag.Get(s.TagName))
|
||||
|
||||
// if the value is a zero value and the field is marked as omitempty do
|
||||
// not include
|
||||
if tagOpts.Has("omitempty") {
|
||||
zero := reflect.Zero(val.Type()).Interface()
|
||||
current := val.Interface()
|
||||
|
||||
if reflect.DeepEqual(current, zero) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if tagOpts.Has("string") {
|
||||
s, ok := val.Interface().(fmt.Stringer)
|
||||
if ok {
|
||||
t = append(t, s.String())
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if IsStruct(val.Interface()) && !tagOpts.Has("omitnested") {
|
||||
// look out for embedded structs, and convert them to a
|
||||
// []interface{} to be added to the final values slice
|
||||
for _, embeddedVal := range Values(val.Interface()) {
|
||||
t = append(t, embeddedVal)
|
||||
}
|
||||
} else {
|
||||
t = append(t, val.Interface())
|
||||
}
|
||||
}
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
// Fields returns a slice of Fields. A struct tag with the content of "-"
|
||||
// ignores the checking of that particular field. Example:
|
||||
//
|
||||
// // Field is ignored by this package.
|
||||
// Field bool `structs:"-"`
|
||||
//
|
||||
// It panics if s's kind is not struct.
|
||||
func (s *Struct) Fields() []*Field {
|
||||
return getFields(s.value, s.TagName)
|
||||
}
|
||||
|
||||
// Names returns a slice of field names. A struct tag with the content of "-"
|
||||
// ignores the checking of that particular field. Example:
|
||||
//
|
||||
// // Field is ignored by this package.
|
||||
// Field bool `structs:"-"`
|
||||
//
|
||||
// It panics if s's kind is not struct.
|
||||
func (s *Struct) Names() []string {
|
||||
fields := getFields(s.value, s.TagName)
|
||||
|
||||
names := make([]string, len(fields))
|
||||
|
||||
for i, field := range fields {
|
||||
names[i] = field.Name()
|
||||
}
|
||||
|
||||
return names
|
||||
}
|
||||
|
||||
func getFields(v reflect.Value, tagName string) []*Field {
|
||||
if v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
|
||||
t := v.Type()
|
||||
|
||||
var fields []*Field
|
||||
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
field := t.Field(i)
|
||||
|
||||
if tag := field.Tag.Get(tagName); tag == "-" {
|
||||
continue
|
||||
}
|
||||
|
||||
f := &Field{
|
||||
field: field,
|
||||
value: v.FieldByName(field.Name),
|
||||
}
|
||||
|
||||
fields = append(fields, f)
|
||||
|
||||
}
|
||||
|
||||
return fields
|
||||
}
|
||||
|
||||
// Field returns a new Field struct that provides several high level functions
|
||||
// around a single struct field entity. It panics if the field is not found.
|
||||
func (s *Struct) Field(name string) *Field {
|
||||
f, ok := s.FieldOk(name)
|
||||
if !ok {
|
||||
panic("field not found")
|
||||
}
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
// FieldOk returns a new Field struct that provides several high level functions
|
||||
// around a single struct field entity. The boolean returns true if the field
|
||||
// was found.
|
||||
func (s *Struct) FieldOk(name string) (*Field, bool) {
|
||||
t := s.value.Type()
|
||||
|
||||
field, ok := t.FieldByName(name)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return &Field{
|
||||
field: field,
|
||||
value: s.value.FieldByName(name),
|
||||
defaultTag: s.TagName,
|
||||
}, true
|
||||
}
|
||||
|
||||
// IsZero returns true if all fields in a struct is a zero value (not
|
||||
// initialized) A struct tag with the content of "-" ignores the checking of
|
||||
// that particular field. Example:
|
||||
//
|
||||
// // Field is ignored by this package.
|
||||
// Field bool `structs:"-"`
|
||||
//
|
||||
// A value with the option of "omitnested" stops iterating further if the type
|
||||
// is a struct. Example:
|
||||
//
|
||||
// // Field is not processed further by this package.
|
||||
// Field time.Time `structs:"myName,omitnested"`
|
||||
// Field *http.Request `structs:",omitnested"`
|
||||
//
|
||||
// Note that only exported fields of a struct can be accessed, non exported
|
||||
// fields will be neglected. It panics if s's kind is not struct.
|
||||
func (s *Struct) IsZero() bool {
|
||||
fields := s.structFields()
|
||||
|
||||
for _, field := range fields {
|
||||
val := s.value.FieldByName(field.Name)
|
||||
|
||||
_, tagOpts := parseTag(field.Tag.Get(s.TagName))
|
||||
|
||||
if IsStruct(val.Interface()) && !tagOpts.Has("omitnested") {
|
||||
ok := IsZero(val.Interface())
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
// zero value of the given field, such as "" for string, 0 for int
|
||||
zero := reflect.Zero(val.Type()).Interface()
|
||||
|
||||
// current value of the given field
|
||||
current := val.Interface()
|
||||
|
||||
if !reflect.DeepEqual(current, zero) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// HasZero returns true if a field in a struct is not initialized (zero value).
|
||||
// A struct tag with the content of "-" ignores the checking of that particular
|
||||
// field. Example:
|
||||
//
|
||||
// // Field is ignored by this package.
|
||||
// Field bool `structs:"-"`
|
||||
//
|
||||
// A value with the option of "omitnested" stops iterating further if the type
|
||||
// is a struct. Example:
|
||||
//
|
||||
// // Field is not processed further by this package.
|
||||
// Field time.Time `structs:"myName,omitnested"`
|
||||
// Field *http.Request `structs:",omitnested"`
|
||||
//
|
||||
// Note that only exported fields of a struct can be accessed, non exported
|
||||
// fields will be neglected. It panics if s's kind is not struct.
|
||||
func (s *Struct) HasZero() bool {
|
||||
fields := s.structFields()
|
||||
|
||||
for _, field := range fields {
|
||||
val := s.value.FieldByName(field.Name)
|
||||
|
||||
_, tagOpts := parseTag(field.Tag.Get(s.TagName))
|
||||
|
||||
if IsStruct(val.Interface()) && !tagOpts.Has("omitnested") {
|
||||
ok := HasZero(val.Interface())
|
||||
if ok {
|
||||
return true
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
// zero value of the given field, such as "" for string, 0 for int
|
||||
zero := reflect.Zero(val.Type()).Interface()
|
||||
|
||||
// current value of the given field
|
||||
current := val.Interface()
|
||||
|
||||
if reflect.DeepEqual(current, zero) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Name returns the structs's type name within its package. For more info refer
|
||||
// to Name() function.
|
||||
func (s *Struct) Name() string {
|
||||
return s.value.Type().Name()
|
||||
}
|
||||
|
||||
// structFields returns the exported struct fields for a given s struct. This
|
||||
// is a convenient helper method to avoid duplicate code in some of the
|
||||
// functions.
|
||||
func (s *Struct) structFields() []reflect.StructField {
|
||||
t := s.value.Type()
|
||||
|
||||
var f []reflect.StructField
|
||||
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
field := t.Field(i)
|
||||
// we can't access the value of unexported fields
|
||||
if field.PkgPath != "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// don't check if it's omitted
|
||||
if tag := field.Tag.Get(s.TagName); tag == "-" {
|
||||
continue
|
||||
}
|
||||
|
||||
f = append(f, field)
|
||||
}
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
func strctVal(s interface{}) reflect.Value {
|
||||
v := reflect.ValueOf(s)
|
||||
|
||||
// if pointer get the underlying element≤
|
||||
for v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
|
||||
if v.Kind() != reflect.Struct {
|
||||
panic("not struct")
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
// Map converts the given struct to a map[string]interface{}. For more info
|
||||
// refer to Struct types Map() method. It panics if s's kind is not struct.
|
||||
func Map(s interface{}) map[string]interface{} {
|
||||
return New(s).Map()
|
||||
}
|
||||
|
||||
// FillMap is the same as Map. Instead of returning the output, it fills the
|
||||
// given map.
|
||||
func FillMap(s interface{}, out map[string]interface{}) {
|
||||
New(s).FillMap(out)
|
||||
}
|
||||
|
||||
// Values converts the given struct to a []interface{}. For more info refer to
|
||||
// Struct types Values() method. It panics if s's kind is not struct.
|
||||
func Values(s interface{}) []interface{} {
|
||||
return New(s).Values()
|
||||
}
|
||||
|
||||
// Fields returns a slice of *Field. For more info refer to Struct types
|
||||
// Fields() method. It panics if s's kind is not struct.
|
||||
func Fields(s interface{}) []*Field {
|
||||
return New(s).Fields()
|
||||
}
|
||||
|
||||
// Names returns a slice of field names. For more info refer to Struct types
|
||||
// Names() method. It panics if s's kind is not struct.
|
||||
func Names(s interface{}) []string {
|
||||
return New(s).Names()
|
||||
}
|
||||
|
||||
// IsZero returns true if all fields is equal to a zero value. For more info
|
||||
// refer to Struct types IsZero() method. It panics if s's kind is not struct.
|
||||
func IsZero(s interface{}) bool {
|
||||
return New(s).IsZero()
|
||||
}
|
||||
|
||||
// HasZero returns true if any field is equal to a zero value. For more info
|
||||
// refer to Struct types HasZero() method. It panics if s's kind is not struct.
|
||||
func HasZero(s interface{}) bool {
|
||||
return New(s).HasZero()
|
||||
}
|
||||
|
||||
// IsStruct returns true if the given variable is a struct or a pointer to
|
||||
// struct.
|
||||
func IsStruct(s interface{}) bool {
|
||||
v := reflect.ValueOf(s)
|
||||
if v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
|
||||
// uninitialized zero value of a struct
|
||||
if v.Kind() == reflect.Invalid {
|
||||
return false
|
||||
}
|
||||
|
||||
return v.Kind() == reflect.Struct
|
||||
}
|
||||
|
||||
// Name returns the structs's type name within its package. It returns an
|
||||
// empty string for unnamed types. It panics if s's kind is not struct.
|
||||
func Name(s interface{}) string {
|
||||
return New(s).Name()
|
||||
}
|
||||
|
||||
// nested retrieves recursively all types for the given value and returns the
|
||||
// nested value.
|
||||
func (s *Struct) nested(val reflect.Value) interface{} {
|
||||
var finalVal interface{}
|
||||
|
||||
v := reflect.ValueOf(val.Interface())
|
||||
if v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
|
||||
switch v.Kind() {
|
||||
case reflect.Struct:
|
||||
n := New(val.Interface())
|
||||
n.TagName = s.TagName
|
||||
m := n.Map()
|
||||
|
||||
// do not add the converted value if there are no exported fields, ie:
|
||||
// time.Time
|
||||
if len(m) == 0 {
|
||||
finalVal = val.Interface()
|
||||
} else {
|
||||
finalVal = m
|
||||
}
|
||||
case reflect.Map:
|
||||
v := val.Type().Elem()
|
||||
if v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
|
||||
// only iterate over struct types, ie: map[string]StructType,
|
||||
// map[string][]StructType,
|
||||
if v.Kind() == reflect.Struct ||
|
||||
(v.Kind() == reflect.Slice && v.Elem().Kind() == reflect.Struct) {
|
||||
m := make(map[string]interface{}, val.Len())
|
||||
for _, k := range val.MapKeys() {
|
||||
m[k.String()] = s.nested(val.MapIndex(k))
|
||||
}
|
||||
finalVal = m
|
||||
break
|
||||
}
|
||||
|
||||
// TODO(arslan): should this be optional?
|
||||
finalVal = val.Interface()
|
||||
case reflect.Slice, reflect.Array:
|
||||
if val.Type().Kind() == reflect.Interface {
|
||||
finalVal = val.Interface()
|
||||
break
|
||||
}
|
||||
|
||||
// TODO(arslan): should this be optional?
|
||||
// do not iterate of non struct types, just pass the value. Ie: []int,
|
||||
// []string, co... We only iterate further if it's a struct.
|
||||
// i.e []foo or []*foo
|
||||
if val.Type().Elem().Kind() != reflect.Struct &&
|
||||
!(val.Type().Elem().Kind() == reflect.Ptr &&
|
||||
val.Type().Elem().Elem().Kind() == reflect.Struct) {
|
||||
finalVal = val.Interface()
|
||||
break
|
||||
}
|
||||
|
||||
slices := make([]interface{}, val.Len(), val.Len())
|
||||
for x := 0; x < val.Len(); x++ {
|
||||
slices[x] = s.nested(val.Index(x))
|
||||
}
|
||||
finalVal = slices
|
||||
default:
|
||||
finalVal = val.Interface()
|
||||
}
|
||||
|
||||
return finalVal
|
||||
}
|
||||
32
vendor/github.com/fatih/structs/tags.go
generated
vendored
Normal file
32
vendor/github.com/fatih/structs/tags.go
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
package structs
|
||||
|
||||
import "strings"
|
||||
|
||||
// tagOptions contains a slice of tag options
|
||||
type tagOptions []string
|
||||
|
||||
// Has returns true if the given optiton is available in tagOptions
|
||||
func (t tagOptions) Has(opt string) bool {
|
||||
for _, tagOpt := range t {
|
||||
if tagOpt == opt {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// parseTag splits a struct field's tag into its name and a list of options
|
||||
// which comes after a name. A tag is in the form of: "name,option1,option2".
|
||||
// The name can be neglectected.
|
||||
func parseTag(tag string) (string, tagOptions) {
|
||||
// tag is one of followings:
|
||||
// ""
|
||||
// "name"
|
||||
// "name,opt"
|
||||
// "name,opt,opt2"
|
||||
// ",opt"
|
||||
|
||||
res := strings.Split(tag, ",")
|
||||
return res[0], res[1:]
|
||||
}
|
||||
1
vendor/github.com/packethost/packngo/metadata/metadata.go
generated
vendored
1
vendor/github.com/packethost/packngo/metadata/metadata.go
generated
vendored
@@ -9,6 +9,7 @@ const (
|
||||
)
|
||||
|
||||
type Metadata struct {
|
||||
PhoneHomeURL string `json:"phone_home_url"`
|
||||
ApiUrl string `json:"api_url"`
|
||||
Id string `json:"id"`
|
||||
Hostname string `json:"hostname"`
|
||||
|
||||
Reference in New Issue
Block a user