Skip to content

Commit

Permalink
refactor Service.Start
Browse files Browse the repository at this point in the history
Signed-off-by: Alex Suraci <alex@dagger.io>
  • Loading branch information
vito committed Jul 18, 2023
1 parent 2180f04 commit 114bff4
Showing 1 changed file with 192 additions and 161 deletions.
353 changes: 192 additions & 161 deletions core/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,9 @@ func solveRef(ctx context.Context, gw bkgw.Client, def *pb.Definition) (bkgw.Ref
return res.Ref, nil
}

// goling: nocyclo
func (svc *Service) Start(ctx context.Context, gw bkgw.Client, progSock *Socket) (running *RunningService, err error) {
ctr := svc.Container
cfg := ctr.Config
opts := svc.Exec

detachDeps, _, err := StartServices(ctx, gw, ctr.Services)
Expand All @@ -169,6 +169,111 @@ func (svc *Service) Start(ctx context.Context, gw bkgw.Client, progSock *Socket)
}
}()

mounts, err := svc.mounts(ctx, gw, progSock)
if err != nil {
return nil, err
}

args, err := ctr.command(opts)
if err != nil {
return nil, err
}

env := svc.env()

secretEnv, mounts, env, err := svc.secrets(mounts, env)
if err != nil {
return nil, err
}

var securityMode pb.SecurityMode
if opts.InsecureRootCapabilities {
securityMode = pb.SecurityMode_INSECURE
}

rec := progrock.RecorderFromContext(ctx)

dig, err := svc.Digest()
if err != nil {
return nil, err
}

host, err := svc.Hostname()
if err != nil {
return nil, err
}

vtx := rec.Vertex(dig, "start "+strings.Join(args, " "))

fullHost := host + "." + SessionDomain()

health := newHealth(gw, fullHost, svc.Container.Ports)

pbPlatform := pb.PlatformFromSpec(ctr.Platform)

gc, err := gw.NewContainer(ctx, bkgw.NewContainerRequest{
Mounts: mounts,
Hostname: fullHost,
Platform: &pbPlatform,
NetworkConfigID: DaggerNetwork,
})
if err != nil {
return nil, err
}

checked := make(chan error, 1)
go func() {
checked <- health.Check(ctx)
}()

svcProc, err := gc.Start(ctx, bkgw.StartRequest{
Args: args,
Env: env,
SecretEnv: secretEnv,
User: cfg.User,
Cwd: cfg.WorkingDir,
Tty: false,
Stdout: nopCloser{vtx.Stdout()},
Stderr: nopCloser{vtx.Stderr()},
SecurityMode: securityMode,
})
if err != nil {
return nil, err
}

exited := make(chan error, 1)
go func() {
exited <- svcProc.Wait()

// detach dependent services when process exits
detachDeps()
}()

select {
case err := <-checked:
if err != nil {
return nil, fmt.Errorf("health check errored: %w", err)
}

return &RunningService{
Service: svc,
Hostname: host,
Container: gc,
Process: svcProc,
}, nil
case err := <-exited:
if err != nil {
return nil, fmt.Errorf("exited: %w", err)
}

return nil, fmt.Errorf("service exited before healthcheck")
}
}

func (svc *Service) mounts(ctx context.Context, gw bkgw.Client, progSock *Socket) ([]bkgw.Mount, error) {
ctr := svc.Container
opts := svc.Exec

fsRef, err := solveRef(ctx, gw, ctr.FS)
if err != nil {
return nil, err
Expand Down Expand Up @@ -215,94 +320,6 @@ func (svc *Service) Start(ctx context.Context, gw bkgw.Client, progSock *Socket)
})
}

pbPlatform := pb.PlatformFromSpec(ctr.Platform)

args, err := ctr.command(opts)
if err != nil {
return nil, err
}

cfg := ctr.Config

env := []string{}

for _, e := range cfg.Env {
// strip out any env that are meant for internal use only, to prevent
// manually setting them
switch {
case strings.HasPrefix(e, "_DAGGER_ENABLE_NESTING="):
case strings.HasPrefix(e, DebugFailedExecEnv+"="):
default:
env = append(env, e)
}
}

secretEnv := []*pb.SecretEnv{}
secretsToScrub := SecretToScrubInfo{}
for i, ctrSecret := range ctr.Secrets {
switch {
case ctrSecret.EnvName != "":
secretsToScrub.Envs = append(secretsToScrub.Envs, ctrSecret.EnvName)
secret, err := ctrSecret.Secret.ToSecret()
if err != nil {
return nil, err
}
secretEnv = append(secretEnv, &pb.SecretEnv{
ID: secret.Name,
Name: ctrSecret.EnvName,
})
case ctrSecret.MountPath != "":
secretsToScrub.Files = append(secretsToScrub.Files, ctrSecret.MountPath)
opt := &pb.SecretOpt{}
if ctrSecret.Owner != nil {
opt.Uid = uint32(ctrSecret.Owner.UID)
opt.Gid = uint32(ctrSecret.Owner.UID)
opt.Mode = 0o400 // preserve default
}
mounts = append(mounts, bkgw.Mount{
Dest: ctrSecret.MountPath,
MountType: pb.MountType_SECRET,
SecretOpt: opt,
})
default:
return nil, fmt.Errorf("malformed secret config at index %d", i)
}
}

if len(secretsToScrub.Envs) != 0 || len(secretsToScrub.Files) != 0 {
// we sort to avoid non-deterministic order that would break caching
sort.Strings(secretsToScrub.Envs)
sort.Strings(secretsToScrub.Files)

secretsToScrubJSON, err := json.Marshal(secretsToScrub)
if err != nil {
return nil, fmt.Errorf("scrub secrets json: %w", err)
}
env = append(env, "_DAGGER_SCRUB_SECRETS="+string(secretsToScrubJSON))
}

for _, socket := range ctr.Sockets {
if socket.UnixPath == "" {
return nil, fmt.Errorf("unsupported socket: only unix paths are implemented")
}

opt := &pb.SSHOpt{
ID: socket.Socket.LLBID(),
}

if socket.Owner != nil {
opt.Uid = uint32(socket.Owner.UID)
opt.Gid = uint32(socket.Owner.UID)
opt.Mode = 0o600 // preserve default
}

mounts = append(mounts, bkgw.Mount{
Dest: socket.UnixPath,
MountType: pb.MountType_SSH,
SSHOpt: opt,
})
}

for _, mnt := range ctr.Mounts {
mount := bkgw.Mount{
Dest: mnt.Target,
Expand Down Expand Up @@ -347,6 +364,49 @@ func (svc *Service) Start(ctx context.Context, gw bkgw.Client, progSock *Socket)
mounts = append(mounts, mount)
}

for _, socket := range ctr.Sockets {
if socket.UnixPath == "" {
return nil, fmt.Errorf("unsupported socket: only unix paths are implemented")
}

opt := &pb.SSHOpt{
ID: socket.Socket.LLBID(),
}

if socket.Owner != nil {
opt.Uid = uint32(socket.Owner.UID)
opt.Gid = uint32(socket.Owner.UID)
opt.Mode = 0o600 // preserve default
}

mounts = append(mounts, bkgw.Mount{
Dest: socket.UnixPath,
MountType: pb.MountType_SSH,
SSHOpt: opt,
})
}

return mounts, nil
}

func (svc *Service) env() []string {
ctr := svc.Container
opts := svc.Exec
cfg := ctr.Config

env := []string{}

for _, e := range cfg.Env {
// strip out any env that are meant for internal use only, to prevent
// manually setting them
switch {
case strings.HasPrefix(e, "_DAGGER_ENABLE_NESTING="):
case strings.HasPrefix(e, DebugFailedExecEnv+"="):
default:
env = append(env, e)
}
}

if opts.ExperimentalPrivilegedNesting {
env = append(env, "_DAGGER_ENABLE_NESTING=")
}
Expand All @@ -363,86 +423,57 @@ func (svc *Service) Start(ctx context.Context, gw bkgw.Client, progSock *Socket)
env = append(env, "_DAGGER_HOSTNAME_ALIAS_"+alias.Alias+"="+alias.Target)
}

var securityMode pb.SecurityMode
if opts.InsecureRootCapabilities {
securityMode = pb.SecurityMode_INSECURE
}

rec := progrock.RecorderFromContext(ctx)

dig, err := svc.Digest()
if err != nil {
return nil, err
}

host, err := svc.Hostname()
if err != nil {
return nil, err
}

vtx := rec.Vertex(dig, "start "+strings.Join(args, " "))

fullHost := host + "." + SessionDomain()

health := newHealth(gw, fullHost, svc.Container.Ports)

gc, err := gw.NewContainer(ctx, bkgw.NewContainerRequest{
Mounts: mounts,
Hostname: fullHost,
Platform: &pbPlatform,
NetworkConfigID: DaggerNetwork,
})
if err != nil {
return nil, err
}
return env
}

checked := make(chan error, 1)
go func() {
checked <- health.Check(ctx)
}()
func (svc *Service) secrets(mounts []bkgw.Mount, env []string) ([]*pb.SecretEnv, []bkgw.Mount, []string, error) {
ctr := svc.Container

svcProc, err := gc.Start(ctx, bkgw.StartRequest{
Args: args,
Env: env,
SecretEnv: secretEnv,
User: cfg.User,
Cwd: cfg.WorkingDir,
Tty: false,
Stdout: nopCloser{vtx.Stdout()},
Stderr: nopCloser{vtx.Stderr()},
SecurityMode: securityMode,
})
if err != nil {
return nil, err
secretEnv := []*pb.SecretEnv{}
secretsToScrub := SecretToScrubInfo{}
for i, ctrSecret := range ctr.Secrets {
switch {
case ctrSecret.EnvName != "":
secretsToScrub.Envs = append(secretsToScrub.Envs, ctrSecret.EnvName)
secret, err := ctrSecret.Secret.ToSecret()
if err != nil {
return nil, nil, nil, err
}
secretEnv = append(secretEnv, &pb.SecretEnv{
ID: secret.Name,
Name: ctrSecret.EnvName,
})
case ctrSecret.MountPath != "":
secretsToScrub.Files = append(secretsToScrub.Files, ctrSecret.MountPath)
opt := &pb.SecretOpt{}
if ctrSecret.Owner != nil {
opt.Uid = uint32(ctrSecret.Owner.UID)
opt.Gid = uint32(ctrSecret.Owner.UID)
opt.Mode = 0o400 // preserve default
}
mounts = append(mounts, bkgw.Mount{
Dest: ctrSecret.MountPath,
MountType: pb.MountType_SECRET,
SecretOpt: opt,
})
default:
return nil, nil, nil, fmt.Errorf("malformed secret config at index %d", i)
}
}

exited := make(chan error, 1)
go func() {
exited <- svcProc.Wait()

// detach dependent services when process exits
detachDeps()
}()

select {
case err := <-checked:
if err != nil {
return nil, fmt.Errorf("health check errored: %w", err)
}
if len(secretsToScrub.Envs) != 0 || len(secretsToScrub.Files) != 0 {
// we sort to avoid non-deterministic order that would break caching
sort.Strings(secretsToScrub.Envs)
sort.Strings(secretsToScrub.Files)

return &RunningService{
Service: svc,
Hostname: host,
Container: gc,
Process: svcProc,
}, nil
case err := <-exited:
secretsToScrubJSON, err := json.Marshal(secretsToScrub)
if err != nil {
return nil, fmt.Errorf("exited: %w", err)
return nil, nil, nil, fmt.Errorf("scrub secrets json: %w", err)
}

return nil, fmt.Errorf("service exited before healthcheck")
env = append(env, "_DAGGER_SCRUB_SECRETS="+string(secretsToScrubJSON))
}

return secretEnv, mounts, env, nil
}

type RunningService struct {
Expand Down

0 comments on commit 114bff4

Please sign in to comment.