...

Source file src/github.com/Microsoft/hcsshim/internal/gcs/container.go

Documentation: github.com/Microsoft/hcsshim/internal/gcs

     1  //go:build windows
     2  
     3  package gcs
     4  
     5  import (
     6  	"context"
     7  	"errors"
     8  	"sync"
     9  	"time"
    10  
    11  	"github.com/Microsoft/hcsshim/internal/cow"
    12  	"github.com/Microsoft/hcsshim/internal/hcs/schema1"
    13  	hcsschema "github.com/Microsoft/hcsshim/internal/hcs/schema2"
    14  	"github.com/Microsoft/hcsshim/internal/log"
    15  	"github.com/Microsoft/hcsshim/internal/oc"
    16  	"go.opencensus.io/trace"
    17  )
    18  
    19  const hrComputeSystemDoesNotExist = 0xc037010e
    20  
    21  // Container implements the cow.Container interface for containers
    22  // created via GuestConnection.
    23  type Container struct {
    24  	gc        *GuestConnection
    25  	id        string
    26  	notifyCh  chan struct{}
    27  	closeCh   chan struct{}
    28  	closeOnce sync.Once
    29  	// waitBlock is the channel used to wait for container shutdown or termination
    30  	waitBlock chan struct{}
    31  	// waitError indicates the container termination error if any
    32  	waitError error
    33  }
    34  
    35  var _ cow.Container = &Container{}
    36  
    37  // CreateContainer creates a container using ID `cid` and `cfg`. The request
    38  // will likely not be cancellable even if `ctx` becomes done.
    39  func (gc *GuestConnection) CreateContainer(ctx context.Context, cid string, config interface{}) (_ *Container, err error) {
    40  	ctx, span := oc.StartSpan(ctx, "gcs::GuestConnection::CreateContainer", oc.WithClientSpanKind)
    41  	defer span.End()
    42  	defer func() { oc.SetSpanStatus(span, err) }()
    43  	span.AddAttributes(trace.StringAttribute("cid", cid))
    44  
    45  	c := &Container{
    46  		gc:        gc,
    47  		id:        cid,
    48  		notifyCh:  make(chan struct{}),
    49  		closeCh:   make(chan struct{}),
    50  		waitBlock: make(chan struct{}),
    51  	}
    52  	err = gc.requestNotify(cid, c.notifyCh)
    53  	if err != nil {
    54  		return nil, err
    55  	}
    56  	req := containerCreate{
    57  		requestBase:     makeRequest(ctx, cid),
    58  		ContainerConfig: anyInString{config},
    59  	}
    60  	var resp containerCreateResponse
    61  	err = gc.brdg.RPC(ctx, rpcCreate, &req, &resp, false)
    62  	if err != nil {
    63  		return nil, err
    64  	}
    65  	go c.waitBackground()
    66  	return c, nil
    67  }
    68  
    69  // CloneContainer just creates the wrappers and sets up notification requests for a
    70  // container that is already running inside the UVM (after cloning).
    71  func (gc *GuestConnection) CloneContainer(ctx context.Context, cid string) (_ *Container, err error) {
    72  	c := &Container{
    73  		gc:        gc,
    74  		id:        cid,
    75  		notifyCh:  make(chan struct{}),
    76  		closeCh:   make(chan struct{}),
    77  		waitBlock: make(chan struct{}),
    78  	}
    79  	err = gc.requestNotify(cid, c.notifyCh)
    80  	if err != nil {
    81  		return nil, err
    82  	}
    83  	go c.waitBackground()
    84  	return c, nil
    85  }
    86  
    87  // OS returns the operating system of the container, "linux" or "windows".
    88  func (c *Container) OS() string {
    89  	return c.gc.os
    90  }
    91  
    92  // IsOCI specifies whether CreateProcess should be called with an OCI
    93  // specification in its input.
    94  func (c *Container) IsOCI() bool {
    95  	return c.gc.os != "windows"
    96  }
    97  
    98  // Close releases associated with the container.
    99  func (c *Container) Close() error {
   100  	c.closeOnce.Do(func() {
   101  		_, span := oc.StartSpan(context.Background(), "gcs::Container::Close")
   102  		defer span.End()
   103  		span.AddAttributes(trace.StringAttribute("cid", c.id))
   104  
   105  		close(c.closeCh)
   106  	})
   107  	return nil
   108  }
   109  
   110  // CreateProcess creates a process in the container.
   111  func (c *Container) CreateProcess(ctx context.Context, config interface{}) (_ cow.Process, err error) {
   112  	ctx, span := oc.StartSpan(ctx, "gcs::Container::CreateProcess", oc.WithClientSpanKind)
   113  	defer span.End()
   114  	defer func() { oc.SetSpanStatus(span, err) }()
   115  	span.AddAttributes(trace.StringAttribute("cid", c.id))
   116  
   117  	return c.gc.exec(ctx, c.id, config)
   118  }
   119  
   120  // ID returns the container's ID.
   121  func (c *Container) ID() string {
   122  	return c.id
   123  }
   124  
   125  // Modify sends a modify request to the container.
   126  func (c *Container) Modify(ctx context.Context, config interface{}) (err error) {
   127  	ctx, span := oc.StartSpan(ctx, "gcs::Container::Modify", oc.WithClientSpanKind)
   128  	defer span.End()
   129  	defer func() { oc.SetSpanStatus(span, err) }()
   130  	span.AddAttributes(trace.StringAttribute("cid", c.id))
   131  
   132  	req := containerModifySettings{
   133  		requestBase: makeRequest(ctx, c.id),
   134  		Request:     config,
   135  	}
   136  	var resp responseBase
   137  	return c.gc.brdg.RPC(ctx, rpcModifySettings, &req, &resp, false)
   138  }
   139  
   140  // Properties returns the requested container properties targeting a V1 schema container.
   141  func (c *Container) Properties(ctx context.Context, types ...schema1.PropertyType) (_ *schema1.ContainerProperties, err error) {
   142  	ctx, span := oc.StartSpan(ctx, "gcs::Container::Properties", oc.WithClientSpanKind)
   143  	defer span.End()
   144  	defer func() { oc.SetSpanStatus(span, err) }()
   145  	span.AddAttributes(trace.StringAttribute("cid", c.id))
   146  
   147  	req := containerGetProperties{
   148  		requestBase: makeRequest(ctx, c.id),
   149  		Query:       containerPropertiesQuery{PropertyTypes: types},
   150  	}
   151  	var resp containerGetPropertiesResponse
   152  	err = c.gc.brdg.RPC(ctx, rpcGetProperties, &req, &resp, true)
   153  	if err != nil {
   154  		return nil, err
   155  	}
   156  	return (*schema1.ContainerProperties)(&resp.Properties), nil
   157  }
   158  
   159  // PropertiesV2 returns the requested container properties targeting a V2 schema container.
   160  func (c *Container) PropertiesV2(ctx context.Context, types ...hcsschema.PropertyType) (_ *hcsschema.Properties, err error) {
   161  	ctx, span := oc.StartSpan(ctx, "gcs::Container::PropertiesV2", oc.WithClientSpanKind)
   162  	defer span.End()
   163  	defer func() { oc.SetSpanStatus(span, err) }()
   164  	span.AddAttributes(trace.StringAttribute("cid", c.id))
   165  
   166  	req := containerGetPropertiesV2{
   167  		requestBase: makeRequest(ctx, c.id),
   168  		Query:       containerPropertiesQueryV2{PropertyTypes: types},
   169  	}
   170  	var resp containerGetPropertiesResponseV2
   171  	err = c.gc.brdg.RPC(ctx, rpcGetProperties, &req, &resp, true)
   172  	if err != nil {
   173  		return nil, err
   174  	}
   175  	return (*hcsschema.Properties)(&resp.Properties), nil
   176  }
   177  
   178  // Start starts the container.
   179  func (c *Container) Start(ctx context.Context) (err error) {
   180  	ctx, span := oc.StartSpan(ctx, "gcs::Container::Start", oc.WithClientSpanKind)
   181  	defer span.End()
   182  	defer func() { oc.SetSpanStatus(span, err) }()
   183  	span.AddAttributes(trace.StringAttribute("cid", c.id))
   184  
   185  	req := makeRequest(ctx, c.id)
   186  	var resp responseBase
   187  	return c.gc.brdg.RPC(ctx, rpcStart, &req, &resp, false)
   188  }
   189  
   190  func (c *Container) shutdown(ctx context.Context, proc rpcProc) error {
   191  	req := makeRequest(ctx, c.id)
   192  	var resp responseBase
   193  	err := c.gc.brdg.RPC(ctx, proc, &req, &resp, true)
   194  	if err != nil {
   195  		if uint32(resp.Result) != hrComputeSystemDoesNotExist {
   196  			return err
   197  		}
   198  		select {
   199  		case <-c.notifyCh:
   200  		default:
   201  			log.G(ctx).WithError(err).Warn("ignoring missing container")
   202  		}
   203  	}
   204  	return nil
   205  }
   206  
   207  // Shutdown sends a graceful shutdown request to the container. The container
   208  // might not be terminated by the time the request completes (and might never
   209  // terminate).
   210  func (c *Container) Shutdown(ctx context.Context) (err error) {
   211  	ctx, span := oc.StartSpan(ctx, "gcs::Container::Shutdown", oc.WithClientSpanKind)
   212  	defer span.End()
   213  	defer func() { oc.SetSpanStatus(span, err) }()
   214  	span.AddAttributes(trace.StringAttribute("cid", c.id))
   215  
   216  	ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
   217  	defer cancel()
   218  	return c.shutdown(ctx, rpcShutdownGraceful)
   219  }
   220  
   221  // Terminate sends a forceful terminate request to the container. The container
   222  // might not be terminated by the time the request completes (and might never
   223  // terminate).
   224  func (c *Container) Terminate(ctx context.Context) (err error) {
   225  	ctx, span := oc.StartSpan(ctx, "gcs::Container::Terminate", oc.WithClientSpanKind)
   226  	defer span.End()
   227  	defer func() { oc.SetSpanStatus(span, err) }()
   228  	span.AddAttributes(trace.StringAttribute("cid", c.id))
   229  
   230  	ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
   231  	defer cancel()
   232  	return c.shutdown(ctx, rpcShutdownForced)
   233  }
   234  
   235  func (c *Container) WaitChannel() <-chan struct{} {
   236  	return c.waitBlock
   237  }
   238  
   239  func (c *Container) WaitError() error {
   240  	return c.waitError
   241  }
   242  
   243  // Wait waits for the container to terminate (or Close to be called, or the
   244  // guest connection to terminate).
   245  func (c *Container) Wait() error {
   246  	<-c.WaitChannel()
   247  	return c.WaitError()
   248  }
   249  
   250  func (c *Container) waitBackground() {
   251  	ctx, span := oc.StartSpan(context.Background(), "gcs::Container::waitBackground")
   252  	defer span.End()
   253  	span.AddAttributes(trace.StringAttribute("cid", c.id))
   254  
   255  	select {
   256  	case <-c.notifyCh:
   257  	case <-c.closeCh:
   258  		c.waitError = errors.New("container closed")
   259  	}
   260  	close(c.waitBlock)
   261  
   262  	log.G(ctx).Debug("container exited")
   263  	oc.SetSpanStatus(span, c.waitError)
   264  }
   265  

View as plain text