1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
|
// Copyright 2018 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package testutil contains utility functions for runsc tests.
package testutil
import (
"context"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"path/filepath"
"time"
"github.com/cenkalti/backoff"
specs "github.com/opencontainers/runtime-spec/specs-go"
"gvisor.googlesource.com/gvisor/runsc/boot"
"gvisor.googlesource.com/gvisor/runsc/specutils"
)
// RaceEnabled is set to true if it was built with '--race' option.
var RaceEnabled = false
// ConfigureExePath configures the executable for runsc in the test environment.
func ConfigureExePath() error {
// runsc is in a directory like: 'runsc/linux_amd64_pure_stripped/runsc'.
// Since I don't want to construct 'linux_amd64_pure_stripped' based on the
// build type, do a quick search for: 'runsc/*/runsc'
exePath := ""
lv1 := "./runsc"
lv1fis, err := ioutil.ReadDir(lv1)
if err != nil {
return err
}
for _, fi := range lv1fis {
if !fi.IsDir() {
continue
}
lv2fis, err := ioutil.ReadDir(filepath.Join(lv1, fi.Name()))
if err != nil {
return err
}
for _, candidate := range lv2fis {
if !candidate.IsDir() && candidate.Name() == "runsc" {
exePath, err = filepath.Abs(filepath.Join(lv1, fi.Name(), candidate.Name()))
if err != nil {
return err
}
break
}
}
}
if exePath == "" {
return fmt.Errorf("path to runsc not found")
}
specutils.ExePath = exePath
return nil
}
// TestConfig return the default configuration to use in tests.
func TestConfig() *boot.Config {
return &boot.Config{
Debug: true,
LogFormat: "text",
LogPackets: true,
Network: boot.NetworkNone,
Strace: true,
MultiContainer: true,
FileAccess: boot.FileAccessProxyExclusive,
}
}
// NewSpecWithArgs creates a simple spec with the given args suitable for use
// in tests.
func NewSpecWithArgs(args ...string) *specs.Spec {
spec := &specs.Spec{
// The host filesystem root is the container root.
Root: &specs.Root{
Path: "/",
Readonly: true,
},
Process: &specs.Process{
Args: args,
Env: []string{
"PATH=" + os.Getenv("PATH"),
},
},
}
return spec
}
// SetupRootDir creates a root directory for containers.
func SetupRootDir() (string, error) {
rootDir, err := ioutil.TempDir("", "containers")
if err != nil {
return "", fmt.Errorf("error creating root dir: %v", err)
}
return rootDir, nil
}
// SetupContainer creates a bundle and root dir for the container, generates a
// test config, and writes the spec to config.json in the bundle dir.
func SetupContainer(spec *specs.Spec, conf *boot.Config) (rootDir, bundleDir string, err error) {
rootDir, err = SetupRootDir()
if err != nil {
return "", "", err
}
bundleDir, err = SetupContainerInRoot(rootDir, spec, conf)
return rootDir, bundleDir, err
}
// SetupContainerInRoot creates a bundle for the container, generates a test
// config, and writes the spec to config.json in the bundle dir.
func SetupContainerInRoot(rootDir string, spec *specs.Spec, conf *boot.Config) (bundleDir string, err error) {
bundleDir, err = ioutil.TempDir("", "bundle")
if err != nil {
return "", fmt.Errorf("error creating bundle dir: %v", err)
}
if err = writeSpec(bundleDir, spec); err != nil {
return "", fmt.Errorf("error writing spec: %v", err)
}
conf.RootDir = rootDir
return bundleDir, nil
}
// writeSpec writes the spec to disk in the given directory.
func writeSpec(dir string, spec *specs.Spec) error {
b, err := json.Marshal(spec)
if err != nil {
return err
}
return ioutil.WriteFile(filepath.Join(dir, "config.json"), b, 0755)
}
// UniqueContainerID generates a unique container id for each test.
//
// The container id is used to create an abstract unix domain socket, which must
// be unique. While the container forbids creating two containers with the same
// name, sometimes between test runs the socket does not get cleaned up quickly
// enough, causing container creation to fail.
func UniqueContainerID() string {
return fmt.Sprintf("test-container-%d", time.Now().UnixNano())
}
// Copy copies file from src to dst.
func Copy(src, dst string) error {
in, err := os.Open(src)
if err != nil {
return err
}
defer in.Close()
out, err := os.Create(dst)
if err != nil {
return err
}
defer out.Close()
_, err = io.Copy(out, in)
return err
}
// Poll is a shorthand function to poll for something with given timeout.
func Poll(cb func() error, timeout time.Duration) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
b := backoff.WithContext(backoff.NewConstantBackOff(100*time.Millisecond), ctx)
return backoff.Retry(cb, b)
}
// WaitForHTTP tries GET requests on a port until the call succeeds or timeout.
func WaitForHTTP(port int, timeout time.Duration) error {
cb := func() error {
_, err := http.Get(fmt.Sprintf("http://localhost:%d/", port))
return err
}
return Poll(cb, timeout)
}
|