windows: Rely on the OCI specs instead of custom type

Signed-off-by: Kenfe-Mickael Laventure <mickael.laventure@gmail.com>
This commit is contained in:
Kenfe-Mickael Laventure 2017-07-13 15:02:24 +02:00
parent bff040d087
commit acf863a04e
No known key found for this signature in database
GPG Key ID: 40CF16616B361216
3 changed files with 111 additions and 124 deletions

View File

@ -42,7 +42,7 @@ func newContainer(ctx context.Context, h *hcs.HCS, id string, spec *RuntimeSpec,
return nil, err return nil, err
} }
hcsCtr, err := h.CreateContainer(ctx, id, spec.OCISpec, spec.Configuration, cio) hcsCtr, err := h.CreateContainer(ctx, id, spec.OCISpec, spec.Configuration.TerminateDuration, cio)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -99,15 +99,15 @@ func (c *container) Start(ctx context.Context) error {
} }
func (c *container) Pause(ctx context.Context) error { func (c *container) Pause(ctx context.Context) error {
if c.ctr.GetConfiguration().UseHyperV == false { if c.ctr.IsHyperV() {
return fmt.Errorf("Windows non-HyperV containers do not support pause")
}
return c.ctr.Pause() return c.ctr.Pause()
}
return errors.New("Windows non-HyperV containers do not support pause")
} }
func (c *container) Resume(ctx context.Context) error { func (c *container) Resume(ctx context.Context) error {
if c.ctr.GetConfiguration().UseHyperV == false { if c.ctr.IsHyperV() == false {
return fmt.Errorf("Windows non-HyperV containers do not support resume") return errors.New("Windows non-HyperV containers do not support resume")
} }
return c.ctr.Resume() return c.ctr.Resume()
} }

View File

@ -58,18 +58,16 @@ func (s *HCS) LoadContainers(ctx context.Context) ([]*Container, error) {
hcs: s, hcs: s,
io: &IO{}, io: &IO{},
layerFolderPath: string(b), layerFolderPath: string(b),
conf: Configuration{ terminateDuration: defaultTerminateTimeout,
TerminateDuration: defaultTerminateTimeout,
},
}) })
} }
return containers, nil return containers, nil
} }
func New(owner, rootDir string) *HCS { func New(owner, root string) *HCS {
return &HCS{ return &HCS{
stateDir: rootDir, stateDir: root,
owner: owner, owner: owner,
pidPool: pid.NewPool(), pidPool: pid.NewPool(),
} }
@ -81,7 +79,7 @@ type HCS struct {
pidPool *pid.Pool pidPool *pid.Pool
} }
func (s *HCS) CreateContainer(ctx context.Context, id string, spec specs.Spec, conf Configuration, io *IO) (c *Container, err error) { func (s *HCS) CreateContainer(ctx context.Context, id string, spec specs.Spec, terminateDuration time.Duration, io *IO) (c *Container, err error) {
pid, err := s.pidPool.Get() pid, err := s.pidPool.Get()
if err != nil { if err != nil {
return nil, err return nil, err
@ -102,30 +100,30 @@ func (s *HCS) CreateContainer(ctx context.Context, id string, spec specs.Spec, c
} }
}() }()
if conf.TerminateDuration == 0 { if terminateDuration == 0 {
conf.TerminateDuration = defaultTerminateTimeout terminateDuration = defaultTerminateTimeout
} }
ctrConf, err := newContainerConfig(s.owner, id, spec, conf) conf, err := newContainerConfig(ctx, s.owner, id, spec)
if err != nil { if err != nil {
return nil, err return nil, err
} }
layerPathFile := filepath.Join(stateDir, layerFile) layerPathFile := filepath.Join(stateDir, layerFile)
if err := ioutil.WriteFile(layerPathFile, []byte(ctrConf.LayerFolderPath), 0644); err != nil { if err := ioutil.WriteFile(layerPathFile, []byte(conf.LayerFolderPath), 0644); err != nil {
log.G(ctx).WithError(err).Warnf("failed to save active layer %s", ctrConf.LayerFolderPath) log.G(ctx).WithError(err).Warnf("failed to save active layer %s", conf.LayerFolderPath)
} }
ctr, err := hcsshim.CreateContainer(id, ctrConf) ctr, err := hcsshim.CreateContainer(id, conf)
if err != nil { if err != nil {
removeLayer(ctx, ctrConf.LayerFolderPath) removeLayer(ctx, conf.LayerFolderPath)
return nil, errors.Wrapf(err, "failed to create container %s", id) return nil, errors.Wrapf(err, "failed to create container %s", id)
} }
err = ctr.Start() err = ctr.Start()
if err != nil { if err != nil {
ctr.Terminate() ctr.Terminate()
removeLayer(ctx, ctrConf.LayerFolderPath) removeLayer(ctx, conf.LayerFolderPath)
return nil, errors.Wrapf(err, "failed to start container %s", id) return nil, errors.Wrapf(err, "failed to start container %s", id)
} }
@ -134,11 +132,11 @@ func (s *HCS) CreateContainer(ctx context.Context, id string, spec specs.Spec, c
id: id, id: id,
pid: pid, pid: pid,
spec: spec, spec: spec,
conf: conf,
stateDir: stateDir, stateDir: stateDir,
io: io, io: io,
hcs: s, hcs: s,
layerFolderPath: ctrConf.LayerFolderPath, conf: conf,
terminateDuration: terminateDuration,
processes: make([]*Process, 0), processes: make([]*Process, 0),
}, nil }, nil
} }
@ -147,11 +145,13 @@ type Container struct {
sync.Mutex sync.Mutex
hcsshim.Container hcsshim.Container
conf *hcsshim.ContainerConfig
terminateDuration time.Duration
id string id string
stateDir string stateDir string
pid uint32 pid uint32
spec specs.Spec spec specs.Spec
conf Configuration
io *IO io *IO
hcs *HCS hcs *HCS
layerFolderPath string layerFolderPath string
@ -179,7 +179,7 @@ func (c *Container) Start(ctx context.Context) error {
func (c *Container) getDeathErr(err error) error { func (c *Container) getDeathErr(err error) error {
switch { switch {
case hcsshim.IsPending(err): case hcsshim.IsPending(err):
err = c.WaitTimeout(c.conf.TerminateDuration) err = c.WaitTimeout(c.terminateDuration)
case hcsshim.IsAlreadyStopped(err): case hcsshim.IsAlreadyStopped(err):
err = nil err = nil
} }
@ -265,7 +265,7 @@ func (c *Container) Delete(ctx context.Context) {
return return
} }
serviceCtr, err := c.hcs.CreateContainer(ctx, c.id+"_servicing", c.spec, c.conf, &IO{}) serviceCtr, err := c.hcs.CreateContainer(ctx, c.id+"_servicing", c.spec, c.terminateDuration, &IO{})
if err != nil { if err != nil {
log.G(ctx).WithError(err).WithField("id", c.id).Warn("could not create servicing container") log.G(ctx).WithError(err).WithField("id", c.id).Warn("could not create servicing container")
return return
@ -299,8 +299,8 @@ func (c *Container) ExitCode() (uint32, error) {
return c.processes[0].ExitCode() return c.processes[0].ExitCode()
} }
func (c *Container) GetConfiguration() Configuration { func (c *Container) IsHyperV() bool {
return c.conf return c.conf.HvPartition
} }
func (c *Container) AddProcess(ctx context.Context, id string, spec *specs.Process, io *IO) (*Process, error) { func (c *Container) AddProcess(ctx context.Context, id string, spec *specs.Process, io *IO) (*Process, error) {
@ -416,30 +416,36 @@ func (c *Container) addProcess(ctx context.Context, id string, spec *specs.Proce
return p, nil return p, nil
} }
// newHCSConfiguration generates a hcsshim configuration from the instance // newContainerConfig generates a hcsshim container configuration from the
// OCI Spec and hcs.Configuration. // provided OCI Spec
func newContainerConfig(owner, id string, spec specs.Spec, conf Configuration) (*hcsshim.ContainerConfig, error) { func newContainerConfig(ctx context.Context, owner, id string, spec specs.Spec) (*hcsshim.ContainerConfig, error) {
configuration := &hcsshim.ContainerConfig{ if len(spec.Windows.LayerFolders) == 0 {
return nil, errors.New("LayerFolders cannot be empty")
}
var (
layerFolders = spec.Windows.LayerFolders
conf = &hcsshim.ContainerConfig{
SystemType: "Container", SystemType: "Container",
Name: id, Name: id,
Owner: owner, Owner: owner,
HostName: spec.Hostname, HostName: spec.Hostname,
IgnoreFlushesDuringBoot: conf.IgnoreFlushesDuringBoot, IgnoreFlushesDuringBoot: spec.Windows.IgnoreFlushesDuringBoot,
HvPartition: conf.UseHyperV, AllowUnqualifiedDNSQuery: spec.Windows.Network.AllowUnqualifiedDNSQuery,
AllowUnqualifiedDNSQuery: conf.AllowUnqualifiedDNSQuery, EndpointList: spec.Windows.Network.EndpointList,
EndpointList: conf.NetworkEndpoints, NetworkSharedContainerName: spec.Windows.Network.NetworkSharedContainerName,
NetworkSharedContainerName: conf.NetworkSharedContainerID, Credentials: spec.Windows.CredentialSpec.(string),
Credentials: conf.Credentials,
} }
)
// TODO: use the create request Mount for those // TODO: use the create request Mount for those
for _, layerPath := range conf.Layers { for _, layerPath := range layerFolders {
_, filename := filepath.Split(layerPath) _, filename := filepath.Split(layerPath)
guid, err := hcsshim.NameToGuid(filename) guid, err := hcsshim.NameToGuid(filename)
if err != nil { if err != nil {
return nil, err return nil, err
} }
configuration.Layers = append(configuration.Layers, hcsshim.Layer{ conf.Layers = append(conf.Layers, hcsshim.Layer{
ID: guid.ToString(), ID: guid.ToString(),
Path: layerPath, Path: layerPath,
}) })
@ -459,19 +465,20 @@ func newContainerConfig(owner, id string, spec specs.Spec, conf Configuration) (
} }
} }
} }
configuration.MappedDirectories = mds conf.MappedDirectories = mds
} }
if conf.DNSSearchList != nil { if spec.Windows.Network.DNSSearchList != nil {
configuration.DNSSearchList = strings.Join(conf.DNSSearchList, ",") conf.DNSSearchList = strings.Join(spec.Windows.Network.DNSSearchList, ",")
} }
if configuration.HvPartition { if spec.Windows.HyperV != nil {
for _, layerPath := range conf.Layers { conf.HvPartition = true
utilityVMPath := filepath.Join(layerPath, "UtilityVM") for _, layerPath := range layerFolders {
utilityVMPath := spec.Windows.HyperV.UtilityVMPath
_, err := os.Stat(utilityVMPath) _, err := os.Stat(utilityVMPath)
if err == nil { if err == nil {
configuration.HvRuntime = &hcsshim.HvRuntime{ImagePath: utilityVMPath} conf.HvRuntime = &hcsshim.HvRuntime{ImagePath: utilityVMPath}
break break
} else if !os.IsNotExist(err) { } else if !os.IsNotExist(err) {
return nil, errors.Wrapf(err, "failed to access layer %s", layerPath) return nil, errors.Wrapf(err, "failed to access layer %s", layerPath)
@ -479,78 +486,71 @@ func newContainerConfig(owner, id string, spec specs.Spec, conf Configuration) (
} }
} }
if len(configuration.Layers) == 0 { var (
// TODO: support starting with 0 layers, this mean we need the "filter" directory as parameter err error
return nil, errors.New("at least one layers must be provided") di = hcsshim.DriverInfo{
}
di := hcsshim.DriverInfo{
Flavour: 1, // filter driver Flavour: 1, // filter driver
HomeDir: filepath.Dir(layerFolders[0]),
} }
)
if len(configuration.Layers) > 0 {
di.HomeDir = filepath.Dir(conf.Layers[0])
}
// Windows doesn't support creating a container with a readonly // Windows doesn't support creating a container with a readonly
// filesystem, so always create a RW one // filesystem, so always create a RW one
if err := hcsshim.CreateSandboxLayer(di, id, conf.Layers[0], conf.Layers); err != nil { if err = hcsshim.CreateSandboxLayer(di, id, layerFolders[0], layerFolders); err != nil {
return nil, errors.Wrapf(err, "failed to create sandbox layer for %s: layers: %#v, driverInfo: %#v", return nil, errors.Wrapf(err, "failed to create sandbox layer for %s: layers: %#v, driverInfo: %#v",
id, configuration.Layers, di) id, layerFolders, di)
} }
configuration.LayerFolderPath = filepath.Join(di.HomeDir, id) conf.LayerFolderPath = filepath.Join(di.HomeDir, id)
defer func() {
err := hcsshim.ActivateLayer(di, id)
if err != nil { if err != nil {
removeLayer(context.TODO(), configuration.LayerFolderPath) removeLayer(ctx, conf.LayerFolderPath)
return nil, errors.Wrapf(err, "failed to active layer %s", configuration.LayerFolderPath) }
}()
if err = hcsshim.ActivateLayer(di, id); err != nil {
return nil, errors.Wrapf(err, "failed to activate layer %s", conf.LayerFolderPath)
} }
err = hcsshim.PrepareLayer(di, id, conf.Layers) if err = hcsshim.PrepareLayer(di, id, layerFolders); err != nil {
if err != nil { return nil, errors.Wrapf(err, "failed to prepare layer %s", conf.LayerFolderPath)
removeLayer(context.TODO(), configuration.LayerFolderPath)
return nil, errors.Wrapf(err, "failed to prepare layer %s", configuration.LayerFolderPath)
} }
volumePath, err := hcsshim.GetLayerMountPath(di, id) conf.VolumePath, err = hcsshim.GetLayerMountPath(di, id)
if err != nil { if err != nil {
if err := hcsshim.DestroyLayer(di, id); err != nil {
log.L.Warnf("failed to DestroyLayer %s: %s", id, err)
}
return nil, errors.Wrapf(err, "failed to getmount path for layer %s: driverInfo: %#v", id, di) return nil, errors.Wrapf(err, "failed to getmount path for layer %s: driverInfo: %#v", id, di)
} }
configuration.VolumePath = volumePath
return configuration, nil return conf, nil
} }
// removeLayer deletes the given layer, all associated containers must have // removeLayer deletes the given layer, all associated containers must have
// been shutdown for this to succeed. // been shutdown for this to succeed.
func removeLayer(ctx context.Context, path string) error { func removeLayer(ctx context.Context, path string) error {
layerID := filepath.Base(path) var (
parentPath := filepath.Dir(path) err error
di := hcsshim.DriverInfo{ layerID = filepath.Base(path)
parentPath = filepath.Dir(path)
di = hcsshim.DriverInfo{
Flavour: 1, // filter driver Flavour: 1, // filter driver
HomeDir: parentPath, HomeDir: parentPath,
} }
)
err := hcsshim.UnprepareLayer(di, layerID) if err = hcsshim.UnprepareLayer(di, layerID); err != nil {
if err != nil {
log.G(ctx).WithError(err).Warnf("failed to unprepare layer %s for removal", path) log.G(ctx).WithError(err).Warnf("failed to unprepare layer %s for removal", path)
} }
err = hcsshim.DeactivateLayer(di, layerID) if err = hcsshim.DeactivateLayer(di, layerID); err != nil {
if err != nil {
log.G(ctx).WithError(err).Warnf("failed to deactivate layer %s for removal", path) log.G(ctx).WithError(err).Warnf("failed to deactivate layer %s for removal", path)
} }
removePath := filepath.Join(parentPath, fmt.Sprintf("%s-removing", layerID)) removePath := filepath.Join(parentPath, fmt.Sprintf("%s-removing", layerID))
err = os.Rename(path, removePath) if err = os.Rename(path, removePath); err != nil {
if err != nil {
log.G(ctx).WithError(err).Warnf("failed to rename container layer %s for removal", path) log.G(ctx).WithError(err).Warnf("failed to rename container layer %s for removal", path)
removePath = path removePath = path
} }
if err := hcsshim.DestroyLayer(di, removePath); err != nil {
if err = hcsshim.DestroyLayer(di, removePath); err != nil {
log.G(ctx).WithError(err).Errorf("failed to remove container layer %s", removePath) log.G(ctx).WithError(err).Errorf("failed to remove container layer %s", removePath)
return err return err
} }

View File

@ -5,18 +5,5 @@ package hcs
import "time" import "time"
type Configuration struct { type Configuration struct {
UseHyperV bool `json:"useHyperV,omitempty"`
Layers []string `json:"layers"`
TerminateDuration time.Duration `json:"terminateDuration,omitempty"` TerminateDuration time.Duration `json:"terminateDuration,omitempty"`
IgnoreFlushesDuringBoot bool `json:"ignoreFlushesDuringBoot,omitempty"`
AllowUnqualifiedDNSQuery bool `json:"allowUnqualifiedDNSQuery,omitempty"`
DNSSearchList []string `json:"dnsSearchList,omitempty"`
NetworkEndpoints []string `json:"networkEndpoints,omitempty"`
NetworkSharedContainerID string
Credentials string `json:"credentials,omitempty"`
} }