- fix lint issues
- use errors pkg for wrapping errors
- cleanup on error when setting up secrets mount
- fix erroneous import
- remove unneeded switch for secret reference mode
- return single mount for secrets instead of slice
Signed-off-by: Evan Hazlett <ejhazlett@gmail.com>
| ... | ... |
@@ -3,6 +3,7 @@ package service |
| 3 | 3 |
import ( |
| 4 | 4 |
"context" |
| 5 | 5 |
"fmt" |
| 6 |
+ "path/filepath" |
|
| 6 | 7 |
"strings" |
| 7 | 8 |
|
| 8 | 9 |
"github.com/docker/docker/api/types" |
| ... | ... |
@@ -31,6 +32,13 @@ func parseSecretString(secretString string) (string, string, error) {
|
| 31 | 31 |
} else {
|
| 32 | 32 |
targetName = secretName |
| 33 | 33 |
} |
| 34 |
+ |
|
| 35 |
+ // ensure target is a filename only; no paths allowed |
|
| 36 |
+ tDir, _ := filepath.Split(targetName) |
|
| 37 |
+ if tDir != "" {
|
|
| 38 |
+ return "", "", fmt.Errorf("target must not have a path")
|
|
| 39 |
+ } |
|
| 40 |
+ |
|
| 34 | 41 |
return secretName, targetName, nil |
| 35 | 42 |
} |
| 36 | 43 |
|
| ... | ... |
@@ -22,8 +22,8 @@ import ( |
| 22 | 22 |
"golang.org/x/sys/unix" |
| 23 | 23 |
) |
| 24 | 24 |
|
| 25 |
-// DefaultSHMSize is the default size (64MB) of the SHM which will be mounted in the container |
|
| 26 | 25 |
const ( |
| 26 |
+ // DefaultSHMSize is the default size (64MB) of the SHM which will be mounted in the container |
|
| 27 | 27 |
DefaultSHMSize int64 = 67108864 |
| 28 | 28 |
containerSecretMountPath = "/run/secrets" |
| 29 | 29 |
) |
| ... | ... |
@@ -178,6 +178,7 @@ func (container *Container) NetworkMounts() []Mount {
|
| 178 | 178 |
return mounts |
| 179 | 179 |
} |
| 180 | 180 |
|
| 181 |
+// SecretMountPath returns the path of the secret mount for the container |
|
| 181 | 182 |
func (container *Container) SecretMountPath() string {
|
| 182 | 183 |
return filepath.Join(container.Root, "secrets") |
| 183 | 184 |
} |
| ... | ... |
@@ -267,19 +268,19 @@ func (container *Container) IpcMounts() []Mount {
|
| 267 | 267 |
return mounts |
| 268 | 268 |
} |
| 269 | 269 |
|
| 270 |
-// SecretMounts returns the list of Secret mounts |
|
| 271 |
-func (container *Container) SecretMounts() []Mount {
|
|
| 272 |
- var mounts []Mount |
|
| 270 |
+// SecretMount returns the list of Secret mounts |
|
| 271 |
+func (container *Container) SecretMount() Mount {
|
|
| 272 |
+ var mount Mount |
|
| 273 | 273 |
|
| 274 | 274 |
if len(container.Secrets) > 0 {
|
| 275 |
- mounts = append(mounts, Mount{
|
|
| 275 |
+ mount = Mount{
|
|
| 276 | 276 |
Source: container.SecretMountPath(), |
| 277 | 277 |
Destination: containerSecretMountPath, |
| 278 | 278 |
Writable: false, |
| 279 |
- }) |
|
| 279 |
+ } |
|
| 280 | 280 |
} |
| 281 | 281 |
|
| 282 |
- return mounts |
|
| 282 |
+ return mount |
|
| 283 | 283 |
} |
| 284 | 284 |
|
| 285 | 285 |
// UnmountSecrets unmounts the local tmpfs for secrets |
| ... | ... |
@@ -79,12 +79,9 @@ func containerSpecFromGRPC(c *swarmapi.ContainerSpec) types.ContainerSpec {
|
| 79 | 79 |
func secretReferencesToGRPC(sr []*types.SecretReference) []*swarmapi.SecretReference {
|
| 80 | 80 |
refs := []*swarmapi.SecretReference{}
|
| 81 | 81 |
for _, s := range sr {
|
| 82 |
- var mode swarmapi.SecretReference_Mode |
|
| 83 |
- switch s.Mode {
|
|
| 84 |
- case types.SecretReferenceSystem: |
|
| 82 |
+ mode := swarmapi.SecretReference_FILE |
|
| 83 |
+ if s.Mode == types.SecretReferenceSystem {
|
|
| 85 | 84 |
mode = swarmapi.SecretReference_SYSTEM |
| 86 |
- default: |
|
| 87 |
- mode = swarmapi.SecretReference_FILE |
|
| 88 | 85 |
} |
| 89 | 86 |
refs = append(refs, &swarmapi.SecretReference{
|
| 90 | 87 |
SecretID: s.SecretID, |
| ... | ... |
@@ -1,7 +1,6 @@ |
| 1 | 1 |
package convert |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
- "github.com/Sirupsen/logrus" |
|
| 5 | 4 |
swarmtypes "github.com/docker/docker/api/types/swarm" |
| 6 | 5 |
swarmapi "github.com/docker/swarmkit/api" |
| 7 | 6 |
"github.com/docker/swarmkit/protobuf/ptypes" |
| ... | ... |
@@ -9,7 +8,6 @@ import ( |
| 9 | 9 |
|
| 10 | 10 |
// SecretFromGRPC converts a grpc Secret to a Secret. |
| 11 | 11 |
func SecretFromGRPC(s *swarmapi.Secret) swarmtypes.Secret {
|
| 12 |
- logrus.Debugf("%+v", s)
|
|
| 13 | 12 |
secret := swarmtypes.Secret{
|
| 14 | 13 |
ID: s.ID, |
| 15 | 14 |
Digest: s.Digest, |
| ... | ... |
@@ -33,14 +31,12 @@ func SecretFromGRPC(s *swarmapi.Secret) swarmtypes.Secret {
|
| 33 | 33 |
} |
| 34 | 34 |
|
| 35 | 35 |
// SecretSpecToGRPC converts Secret to a grpc Secret. |
| 36 |
-func SecretSpecToGRPC(s swarmtypes.SecretSpec) (swarmapi.SecretSpec, error) {
|
|
| 37 |
- spec := swarmapi.SecretSpec{
|
|
| 36 |
+func SecretSpecToGRPC(s swarmtypes.SecretSpec) swarmapi.SecretSpec {
|
|
| 37 |
+ return swarmapi.SecretSpec{
|
|
| 38 | 38 |
Annotations: swarmapi.Annotations{
|
| 39 | 39 |
Name: s.Name, |
| 40 | 40 |
Labels: s.Labels, |
| 41 | 41 |
}, |
| 42 | 42 |
Data: s.Data, |
| 43 | 43 |
} |
| 44 |
- |
|
| 45 |
- return spec, nil |
|
| 46 | 44 |
} |
| ... | ... |
@@ -2,9 +2,9 @@ package container |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 | 4 |
executorpkg "github.com/docker/docker/daemon/cluster/executor" |
| 5 |
+ "github.com/docker/swarmkit/agent/exec" |
|
| 5 | 6 |
"github.com/docker/swarmkit/api" |
| 6 | 7 |
"golang.org/x/net/context" |
| 7 |
- "src/github.com/docker/swarmkit/agent/exec" |
|
| 8 | 8 |
) |
| 9 | 9 |
|
| 10 | 10 |
// networkAttacherController implements agent.Controller against docker's API. |
| ... | ... |
@@ -63,10 +63,7 @@ func (c *Cluster) CreateSecret(s types.SecretSpec) (string, error) {
|
| 63 | 63 |
ctx, cancel := c.getRequestContext() |
| 64 | 64 |
defer cancel() |
| 65 | 65 |
|
| 66 |
- secretSpec, err := convert.SecretSpecToGRPC(s) |
|
| 67 |
- if err != nil {
|
|
| 68 |
- return "", err |
|
| 69 |
- } |
|
| 66 |
+ secretSpec := convert.SecretSpecToGRPC(s) |
|
| 70 | 67 |
|
| 71 | 68 |
r, err := c.node.client.CreateSecret(ctx, |
| 72 | 69 |
&swarmapi.CreateSecretRequest{Spec: &secretSpec})
|
| ... | ... |
@@ -111,10 +108,7 @@ func (c *Cluster) UpdateSecret(id string, version uint64, spec types.SecretSpec) |
| 111 | 111 |
ctx, cancel := c.getRequestContext() |
| 112 | 112 |
defer cancel() |
| 113 | 113 |
|
| 114 |
- secretSpec, err := convert.SecretSpecToGRPC(spec) |
|
| 115 |
- if err != nil {
|
|
| 116 |
- return err |
|
| 117 |
- } |
|
| 114 |
+ secretSpec := convert.SecretSpecToGRPC(spec) |
|
| 118 | 115 |
|
| 119 | 116 |
if _, err := c.client.UpdateSecret(ctx, |
| 120 | 117 |
&swarmapi.UpdateSecretRequest{
|
| ... | ... |
@@ -13,6 +13,7 @@ import ( |
| 13 | 13 |
"time" |
| 14 | 14 |
|
| 15 | 15 |
"github.com/Sirupsen/logrus" |
| 16 |
+ "github.com/cloudflare/cfssl/log" |
|
| 16 | 17 |
containertypes "github.com/docker/docker/api/types/container" |
| 17 | 18 |
"github.com/docker/docker/container" |
| 18 | 19 |
"github.com/docker/docker/daemon/links" |
| ... | ... |
@@ -25,6 +26,7 @@ import ( |
| 25 | 25 |
"github.com/opencontainers/runc/libcontainer/devices" |
| 26 | 26 |
"github.com/opencontainers/runc/libcontainer/label" |
| 27 | 27 |
"github.com/opencontainers/runtime-spec/specs-go" |
| 28 |
+ "github.com/pkg/errors" |
|
| 28 | 29 |
) |
| 29 | 30 |
|
| 30 | 31 |
func u32Ptr(i int64) *uint32 { u := uint32(i); return &u }
|
| ... | ... |
@@ -146,36 +148,58 @@ func (daemon *Daemon) setupSecretDir(c *container.Container) error {
|
| 146 | 146 |
localMountPath := c.SecretMountPath() |
| 147 | 147 |
logrus.Debugf("secrets: setting up secret dir: %s", localMountPath)
|
| 148 | 148 |
|
| 149 |
+ var setupErr error |
|
| 150 |
+ |
|
| 151 |
+ defer func(err error) {
|
|
| 152 |
+ if err != nil {
|
|
| 153 |
+ // cleanup |
|
| 154 |
+ _ = detachMounted(localMountPath) |
|
| 155 |
+ |
|
| 156 |
+ if err := os.RemoveAll(localMountPath); err != nil {
|
|
| 157 |
+ log.Errorf("error cleaning up secret mount: %s", err)
|
|
| 158 |
+ } |
|
| 159 |
+ } |
|
| 160 |
+ }(setupErr) |
|
| 161 |
+ |
|
| 149 | 162 |
// create tmpfs |
| 150 | 163 |
if err := os.MkdirAll(localMountPath, 0700); err != nil {
|
| 151 |
- return fmt.Errorf("error creating secret local mount path: %s", err)
|
|
| 164 |
+ setupErr = errors.Wrap(err, "error creating secret local mount path") |
|
| 152 | 165 |
} |
| 153 | 166 |
if err := mount.Mount("tmpfs", localMountPath, "tmpfs", "nodev"); err != nil {
|
| 154 |
- return fmt.Errorf("unable to setup secret mount: %s", err)
|
|
| 167 |
+ setupErr = errors.Wrap(err, "unable to setup secret mount") |
|
| 155 | 168 |
} |
| 156 | 169 |
|
| 157 | 170 |
for _, s := range c.Secrets {
|
| 158 |
- fPath := filepath.Join(localMountPath, s.Target) |
|
| 171 |
+ // ensure that the target is a filename only; no paths allowed |
|
| 172 |
+ tDir, tPath := filepath.Split(s.Target) |
|
| 173 |
+ if tDir != "" {
|
|
| 174 |
+ setupErr = fmt.Errorf("error creating secret: secret must not have a path")
|
|
| 175 |
+ } |
|
| 176 |
+ |
|
| 177 |
+ fPath := filepath.Join(localMountPath, tPath) |
|
| 159 | 178 |
if err := os.MkdirAll(filepath.Dir(fPath), 0700); err != nil {
|
| 160 |
- return fmt.Errorf("error creating secret mount path: %s", err)
|
|
| 179 |
+ setupErr = errors.Wrap(err, "error creating secret mount path") |
|
| 161 | 180 |
} |
| 162 | 181 |
|
| 163 |
- logrus.Debugf("injecting secret: name=%s path=%s", s.Name, fPath)
|
|
| 182 |
+ logrus.WithFields(logrus.Fields{
|
|
| 183 |
+ "name": s.Name, |
|
| 184 |
+ "path": fPath, |
|
| 185 |
+ }).Debug("injecting secret")
|
|
| 164 | 186 |
if err := ioutil.WriteFile(fPath, s.Data, s.Mode); err != nil {
|
| 165 |
- return fmt.Errorf("error injecting secret: %s", err)
|
|
| 187 |
+ setupErr = errors.Wrap(err, "error injecting secret") |
|
| 166 | 188 |
} |
| 167 | 189 |
|
| 168 | 190 |
if err := os.Chown(fPath, s.Uid, s.Gid); err != nil {
|
| 169 |
- return fmt.Errorf("error setting ownership for secret: %s", err)
|
|
| 191 |
+ setupErr = errors.Wrap(err, "error setting ownership for secret") |
|
| 170 | 192 |
} |
| 171 | 193 |
} |
| 172 | 194 |
|
| 173 | 195 |
// remount secrets ro |
| 174 | 196 |
if err := mount.Mount("tmpfs", localMountPath, "tmpfs", "remount,ro"); err != nil {
|
| 175 |
- return fmt.Errorf("unable to remount secret dir as readonly: %s", err)
|
|
| 197 |
+ setupErr = errors.Wrap(err, "unable to remount secret dir as readonly") |
|
| 176 | 198 |
} |
| 177 | 199 |
|
| 178 |
- return nil |
|
| 200 |
+ return setupErr |
|
| 179 | 201 |
} |
| 180 | 202 |
|
| 181 | 203 |
func killProcessDirectly(container *container.Container) error {
|
| ... | ... |
@@ -718,7 +718,8 @@ func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) {
|
| 718 | 718 |
} |
| 719 | 719 |
ms = append(ms, tmpfsMounts...) |
| 720 | 720 |
|
| 721 |
- ms = append(ms, c.SecretMounts()...) |
|
| 721 |
+ ms = append(ms, c.SecretMount()) |
|
| 722 |
+ |
|
| 722 | 723 |
sort.Sort(mounts(ms)) |
| 723 | 724 |
if err := setMounts(daemon, &s, c, ms); err != nil {
|
| 724 | 725 |
return nil, fmt.Errorf("linux mounts: %v", err)
|
| ... | ... |
@@ -5,8 +5,9 @@ import ( |
| 5 | 5 |
containertypes "github.com/docker/docker/api/types/container" |
| 6 | 6 |
) |
| 7 | 7 |
|
| 8 |
+// SetContainerSecrets sets the container secrets needed |
|
| 8 | 9 |
func (daemon *Daemon) SetContainerSecrets(name string, secrets []*containertypes.ContainerSecret) error {
|
| 9 |
- if !secretsSupported() {
|
|
| 10 |
+ if !secretsSupported() && len(secrets) > 0 {
|
|
| 10 | 11 |
logrus.Warn("secrets are not supported on this platform")
|
| 11 | 12 |
return nil |
| 12 | 13 |
} |
| ... | ... |
@@ -284,6 +284,16 @@ func (d *SwarmDaemon) listServices(c *check.C) []swarm.Service {
|
| 284 | 284 |
return services |
| 285 | 285 |
} |
| 286 | 286 |
|
| 287 |
+func (d *SwarmDaemon) listSecrets(c *check.C) []swarm.Service {
|
|
| 288 |
+ status, out, err := d.SockRequest("GET", "/secrets", nil)
|
|
| 289 |
+ c.Assert(err, checker.IsNil, check.Commentf(string(out))) |
|
| 290 |
+ c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out)))
|
|
| 291 |
+ |
|
| 292 |
+ secrets := []swarm.Secret{}
|
|
| 293 |
+ c.Assert(json.Unmarshal(out, &secrets), checker.IsNil) |
|
| 294 |
+ return services |
|
| 295 |
+} |
|
| 296 |
+ |
|
| 287 | 297 |
func (d *SwarmDaemon) getSwarm(c *check.C) swarm.Swarm {
|
| 288 | 298 |
var sw swarm.Swarm |
| 289 | 299 |
status, out, err := d.SockRequest("GET", "/swarm", nil)
|
| ... | ... |
@@ -1263,3 +1263,27 @@ func (s *DockerSwarmSuite) TestAPISwarmServicesUpdateWithName(c *check.C) {
|
| 1263 | 1263 |
c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out)))
|
| 1264 | 1264 |
waitAndAssert(c, defaultReconciliationTimeout, d.checkActiveContainerCount, checker.Equals, instances) |
| 1265 | 1265 |
} |
| 1266 |
+ |
|
| 1267 |
+func (s *DockerSwarmSuite) TestAPISwarmSecretsEmptyList(c *check.C) {
|
|
| 1268 |
+ d := s.AddDaemon(c, true, true) |
|
| 1269 |
+ |
|
| 1270 |
+ secrets := d.listSecrets(c) |
|
| 1271 |
+ c.Assert(secrets, checker.NotNil) |
|
| 1272 |
+ c.Assert(len(secrets), checker.Equals, 0, check.Commentf("secrets: %#v", secrets))
|
|
| 1273 |
+} |
|
| 1274 |
+ |
|
| 1275 |
+//func (s *DockerSwarmSuite) TestAPISwarmServicesCreate(c *check.C) {
|
|
| 1276 |
+// d := s.AddDaemon(c, true, true) |
|
| 1277 |
+// |
|
| 1278 |
+// instances := 2 |
|
| 1279 |
+// id := d.createService(c, simpleTestService, setInstances(instances)) |
|
| 1280 |
+// waitAndAssert(c, defaultReconciliationTimeout, d.checkActiveContainerCount, checker.Equals, instances) |
|
| 1281 |
+// |
|
| 1282 |
+// service := d.getService(c, id) |
|
| 1283 |
+// instances = 5 |
|
| 1284 |
+// d.updateService(c, service, setInstances(instances)) |
|
| 1285 |
+// waitAndAssert(c, defaultReconciliationTimeout, d.checkActiveContainerCount, checker.Equals, instances) |
|
| 1286 |
+// |
|
| 1287 |
+// d.removeService(c, service.ID) |
|
| 1288 |
+// waitAndAssert(c, defaultReconciliationTimeout, d.checkActiveContainerCount, checker.Equals, 0) |
|
| 1289 |
+//} |