Merge pull request #6917 from mheon/retErr_for_libpod
Remove all instances of named return "err" from Libpod
This commit is contained in:
commit
2ac8c69534
|
|
@ -29,7 +29,7 @@ import (
|
||||||
// Init requires that all dependency containers be started (e.g. pod infra
|
// Init requires that all dependency containers be started (e.g. pod infra
|
||||||
// containers). The `recursive` parameter will, if set to true, start these
|
// containers). The `recursive` parameter will, if set to true, start these
|
||||||
// dependency containers before initializing this container.
|
// dependency containers before initializing this container.
|
||||||
func (c *Container) Init(ctx context.Context, recursive bool) (err error) {
|
func (c *Container) Init(ctx context.Context, recursive bool) error {
|
||||||
span, _ := opentracing.StartSpanFromContext(ctx, "containerInit")
|
span, _ := opentracing.StartSpanFromContext(ctx, "containerInit")
|
||||||
span.SetTag("struct", "container")
|
span.SetTag("struct", "container")
|
||||||
defer span.Finish()
|
defer span.Finish()
|
||||||
|
|
@ -85,7 +85,7 @@ func (c *Container) Init(ctx context.Context, recursive bool) (err error) {
|
||||||
// Start requites that all dependency containers (e.g. pod infra containers) be
|
// Start requites that all dependency containers (e.g. pod infra containers) be
|
||||||
// running before being run. The recursive parameter, if set, will start all
|
// running before being run. The recursive parameter, if set, will start all
|
||||||
// dependencies before starting this container.
|
// dependencies before starting this container.
|
||||||
func (c *Container) Start(ctx context.Context, recursive bool) (err error) {
|
func (c *Container) Start(ctx context.Context, recursive bool) error {
|
||||||
span, _ := opentracing.StartSpanFromContext(ctx, "containerStart")
|
span, _ := opentracing.StartSpanFromContext(ctx, "containerStart")
|
||||||
span.SetTag("struct", "container")
|
span.SetTag("struct", "container")
|
||||||
defer span.Finish()
|
defer span.Finish()
|
||||||
|
|
@ -112,7 +112,7 @@ func (c *Container) Start(ctx context.Context, recursive bool) (err error) {
|
||||||
// Attach call occurs before Start).
|
// Attach call occurs before Start).
|
||||||
// In overall functionality, it is identical to the Start call, with the added
|
// In overall functionality, it is identical to the Start call, with the added
|
||||||
// side effect that an attach session will also be started.
|
// side effect that an attach session will also be started.
|
||||||
func (c *Container) StartAndAttach(ctx context.Context, streams *define.AttachStreams, keys string, resize <-chan remotecommand.TerminalSize, recursive bool) (attachResChan <-chan error, err error) {
|
func (c *Container) StartAndAttach(ctx context.Context, streams *define.AttachStreams, keys string, resize <-chan remotecommand.TerminalSize, recursive bool) (<-chan error, error) {
|
||||||
if !c.batched {
|
if !c.batched {
|
||||||
c.lock.Lock()
|
c.lock.Lock()
|
||||||
defer c.lock.Unlock()
|
defer c.lock.Unlock()
|
||||||
|
|
@ -150,7 +150,7 @@ func (c *Container) StartAndAttach(ctx context.Context, streams *define.AttachSt
|
||||||
}
|
}
|
||||||
|
|
||||||
// RestartWithTimeout restarts a running container and takes a given timeout in uint
|
// RestartWithTimeout restarts a running container and takes a given timeout in uint
|
||||||
func (c *Container) RestartWithTimeout(ctx context.Context, timeout uint) (err error) {
|
func (c *Container) RestartWithTimeout(ctx context.Context, timeout uint) error {
|
||||||
if !c.batched {
|
if !c.batched {
|
||||||
c.lock.Lock()
|
c.lock.Lock()
|
||||||
defer c.lock.Unlock()
|
defer c.lock.Unlock()
|
||||||
|
|
@ -160,7 +160,7 @@ func (c *Container) RestartWithTimeout(ctx context.Context, timeout uint) (err e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = c.checkDependenciesAndHandleError(); err != nil {
|
if err := c.checkDependenciesAndHandleError(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -760,7 +760,7 @@ func (c *Container) Checkpoint(ctx context.Context, options ContainerCheckpointO
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restore restores a container
|
// Restore restores a container
|
||||||
func (c *Container) Restore(ctx context.Context, options ContainerCheckpointOptions) (err error) {
|
func (c *Container) Restore(ctx context.Context, options ContainerCheckpointOptions) error {
|
||||||
logrus.Debugf("Trying to restore container %s", c.ID())
|
logrus.Debugf("Trying to restore container %s", c.ID())
|
||||||
if !c.batched {
|
if !c.batched {
|
||||||
c.lock.Lock()
|
c.lock.Lock()
|
||||||
|
|
|
||||||
|
|
@ -779,25 +779,25 @@ func (c *Container) execOCILog(sessionID string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// create a bundle path and associated files for an exec session
|
// create a bundle path and associated files for an exec session
|
||||||
func (c *Container) createExecBundle(sessionID string) (err error) {
|
func (c *Container) createExecBundle(sessionID string) (retErr error) {
|
||||||
bundlePath := c.execBundlePath(sessionID)
|
bundlePath := c.execBundlePath(sessionID)
|
||||||
if createErr := os.MkdirAll(bundlePath, execDirPermission); createErr != nil {
|
if err := os.MkdirAll(bundlePath, execDirPermission); err != nil {
|
||||||
return createErr
|
return err
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if retErr != nil {
|
||||||
if err2 := os.RemoveAll(bundlePath); err != nil {
|
if err := os.RemoveAll(bundlePath); err != nil {
|
||||||
logrus.Warnf("error removing exec bundle after creation caused another error: %v", err2)
|
logrus.Warnf("error removing exec bundle after creation caused another error: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
if err2 := os.MkdirAll(c.execExitFileDir(sessionID), execDirPermission); err2 != nil {
|
if err := os.MkdirAll(c.execExitFileDir(sessionID), execDirPermission); err != nil {
|
||||||
// The directory is allowed to exist
|
// The directory is allowed to exist
|
||||||
if !os.IsExist(err2) {
|
if !os.IsExist(err) {
|
||||||
err = errors.Wrapf(err2, "error creating OCI runtime exit file path %s", c.execExitFileDir(sessionID))
|
return errors.Wrapf(err, "error creating OCI runtime exit file path %s", c.execExitFileDir(sessionID))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// readExecExitCode reads the exit file for an exec session and returns
|
// readExecExitCode reads the exit file for an exec session and returns
|
||||||
|
|
|
||||||
|
|
@ -210,7 +210,7 @@ func (c *Container) handleExitFile(exitFile string, fi os.FileInfo) error {
|
||||||
// Handle container restart policy.
|
// Handle container restart policy.
|
||||||
// This is called when a container has exited, and was not explicitly stopped by
|
// This is called when a container has exited, and was not explicitly stopped by
|
||||||
// an API call to stop the container or pod it is in.
|
// an API call to stop the container or pod it is in.
|
||||||
func (c *Container) handleRestartPolicy(ctx context.Context) (restarted bool, err error) {
|
func (c *Container) handleRestartPolicy(ctx context.Context) (_ bool, retErr error) {
|
||||||
// If we did not get a restart policy match, exit immediately.
|
// If we did not get a restart policy match, exit immediately.
|
||||||
// Do the same if we're not a policy that restarts.
|
// Do the same if we're not a policy that restarts.
|
||||||
if !c.state.RestartPolicyMatch ||
|
if !c.state.RestartPolicyMatch ||
|
||||||
|
|
@ -241,7 +241,7 @@ func (c *Container) handleRestartPolicy(ctx context.Context) (restarted bool, er
|
||||||
logrus.Debugf("Restarting container %s due to restart policy %s", c.ID(), c.config.RestartPolicy)
|
logrus.Debugf("Restarting container %s due to restart policy %s", c.ID(), c.config.RestartPolicy)
|
||||||
|
|
||||||
// Need to check if dependencies are alive.
|
// Need to check if dependencies are alive.
|
||||||
if err = c.checkDependenciesAndHandleError(); err != nil {
|
if err := c.checkDependenciesAndHandleError(); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -263,9 +263,9 @@ func (c *Container) handleRestartPolicy(ctx context.Context) (restarted bool, er
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if retErr != nil {
|
||||||
if err2 := c.cleanup(ctx); err2 != nil {
|
if err := c.cleanup(ctx); err != nil {
|
||||||
logrus.Errorf("error cleaning up container %s: %v", c.ID(), err2)
|
logrus.Errorf("error cleaning up container %s: %v", c.ID(), err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
@ -768,7 +768,7 @@ func (c *Container) save() error {
|
||||||
// Checks the container is in the right state, then initializes the container in preparation to start the container.
|
// Checks the container is in the right state, then initializes the container in preparation to start the container.
|
||||||
// If recursive is true, each of the containers dependencies will be started.
|
// If recursive is true, each of the containers dependencies will be started.
|
||||||
// Otherwise, this function will return with error if there are dependencies of this container that aren't running.
|
// Otherwise, this function will return with error if there are dependencies of this container that aren't running.
|
||||||
func (c *Container) prepareToStart(ctx context.Context, recursive bool) (err error) {
|
func (c *Container) prepareToStart(ctx context.Context, recursive bool) (retErr error) {
|
||||||
// Container must be created or stopped to be started
|
// Container must be created or stopped to be started
|
||||||
if !c.ensureState(define.ContainerStateConfigured, define.ContainerStateCreated, define.ContainerStateStopped, define.ContainerStateExited) {
|
if !c.ensureState(define.ContainerStateConfigured, define.ContainerStateCreated, define.ContainerStateStopped, define.ContainerStateExited) {
|
||||||
return errors.Wrapf(define.ErrCtrStateInvalid, "container %s must be in Created or Stopped state to be started", c.ID())
|
return errors.Wrapf(define.ErrCtrStateInvalid, "container %s must be in Created or Stopped state to be started", c.ID())
|
||||||
|
|
@ -785,9 +785,9 @@ func (c *Container) prepareToStart(ctx context.Context, recursive bool) (err err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if retErr != nil {
|
||||||
if err2 := c.cleanup(ctx); err2 != nil {
|
if err := c.cleanup(ctx); err != nil {
|
||||||
logrus.Errorf("error cleaning up container %s: %v", c.ID(), err2)
|
logrus.Errorf("error cleaning up container %s: %v", c.ID(), err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
@ -1133,7 +1133,7 @@ func (c *Container) reinit(ctx context.Context, retainRetries bool) error {
|
||||||
// Initialize (if necessary) and start a container
|
// Initialize (if necessary) and start a container
|
||||||
// Performs all necessary steps to start a container that is not running
|
// Performs all necessary steps to start a container that is not running
|
||||||
// Does not lock or check validity
|
// Does not lock or check validity
|
||||||
func (c *Container) initAndStart(ctx context.Context) (err error) {
|
func (c *Container) initAndStart(ctx context.Context) (retErr error) {
|
||||||
// If we are ContainerStateUnknown, throw an error
|
// If we are ContainerStateUnknown, throw an error
|
||||||
if c.state.State == define.ContainerStateUnknown {
|
if c.state.State == define.ContainerStateUnknown {
|
||||||
return errors.Wrapf(define.ErrCtrStateInvalid, "container %s is in an unknown state", c.ID())
|
return errors.Wrapf(define.ErrCtrStateInvalid, "container %s is in an unknown state", c.ID())
|
||||||
|
|
@ -1151,9 +1151,9 @@ func (c *Container) initAndStart(ctx context.Context) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if retErr != nil {
|
||||||
if err2 := c.cleanup(ctx); err2 != nil {
|
if err := c.cleanup(ctx); err != nil {
|
||||||
logrus.Errorf("error cleaning up container %s: %v", c.ID(), err2)
|
logrus.Errorf("error cleaning up container %s: %v", c.ID(), err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
@ -1335,7 +1335,7 @@ func (c *Container) unpause() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Internal, non-locking function to restart a container
|
// Internal, non-locking function to restart a container
|
||||||
func (c *Container) restartWithTimeout(ctx context.Context, timeout uint) (err error) {
|
func (c *Container) restartWithTimeout(ctx context.Context, timeout uint) (retErr error) {
|
||||||
if !c.ensureState(define.ContainerStateConfigured, define.ContainerStateCreated, define.ContainerStateRunning, define.ContainerStateStopped, define.ContainerStateExited) {
|
if !c.ensureState(define.ContainerStateConfigured, define.ContainerStateCreated, define.ContainerStateRunning, define.ContainerStateStopped, define.ContainerStateExited) {
|
||||||
return errors.Wrapf(define.ErrCtrStateInvalid, "unable to restart a container in a paused or unknown state")
|
return errors.Wrapf(define.ErrCtrStateInvalid, "unable to restart a container in a paused or unknown state")
|
||||||
}
|
}
|
||||||
|
|
@ -1372,9 +1372,9 @@ func (c *Container) restartWithTimeout(ctx context.Context, timeout uint) (err e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if retErr != nil {
|
||||||
if err2 := c.cleanup(ctx); err2 != nil {
|
if err := c.cleanup(ctx); err != nil {
|
||||||
logrus.Errorf("error cleaning up container %s: %v", c.ID(), err2)
|
logrus.Errorf("error cleaning up container %s: %v", c.ID(), err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
@ -1696,7 +1696,7 @@ func (c *Container) cleanup(ctx context.Context) error {
|
||||||
|
|
||||||
// delete deletes the container and runs any configured poststop
|
// delete deletes the container and runs any configured poststop
|
||||||
// hooks.
|
// hooks.
|
||||||
func (c *Container) delete(ctx context.Context) (err error) {
|
func (c *Container) delete(ctx context.Context) error {
|
||||||
span, _ := opentracing.StartSpanFromContext(ctx, "delete")
|
span, _ := opentracing.StartSpanFromContext(ctx, "delete")
|
||||||
span.SetTag("struct", "container")
|
span.SetTag("struct", "container")
|
||||||
defer span.Finish()
|
defer span.Finish()
|
||||||
|
|
@ -1715,7 +1715,7 @@ func (c *Container) delete(ctx context.Context) (err error) {
|
||||||
// postDeleteHooks runs the poststop hooks (if any) as specified by
|
// postDeleteHooks runs the poststop hooks (if any) as specified by
|
||||||
// the OCI Runtime Specification (which requires them to run
|
// the OCI Runtime Specification (which requires them to run
|
||||||
// post-delete, despite the stage name).
|
// post-delete, despite the stage name).
|
||||||
func (c *Container) postDeleteHooks(ctx context.Context) (err error) {
|
func (c *Container) postDeleteHooks(ctx context.Context) error {
|
||||||
span, _ := opentracing.StartSpanFromContext(ctx, "postDeleteHooks")
|
span, _ := opentracing.StartSpanFromContext(ctx, "postDeleteHooks")
|
||||||
span.SetTag("struct", "container")
|
span.SetTag("struct", "container")
|
||||||
defer span.Finish()
|
defer span.Finish()
|
||||||
|
|
@ -1838,7 +1838,7 @@ func (c *Container) saveSpec(spec *spec.Spec) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Warning: precreate hooks may alter 'config' in place.
|
// Warning: precreate hooks may alter 'config' in place.
|
||||||
func (c *Container) setupOCIHooks(ctx context.Context, config *spec.Spec) (extensionStageHooks map[string][]spec.Hook, err error) {
|
func (c *Container) setupOCIHooks(ctx context.Context, config *spec.Spec) (map[string][]spec.Hook, error) {
|
||||||
allHooks := make(map[string][]spec.Hook)
|
allHooks := make(map[string][]spec.Hook)
|
||||||
if c.runtime.config.Engine.HooksDir == nil {
|
if c.runtime.config.Engine.HooksDir == nil {
|
||||||
if rootless.IsRootless() {
|
if rootless.IsRootless() {
|
||||||
|
|
@ -1952,7 +1952,7 @@ func (c *Container) checkReadyForRemoval() error {
|
||||||
|
|
||||||
// writeJSONFile marshalls and writes the given data to a JSON file
|
// writeJSONFile marshalls and writes the given data to a JSON file
|
||||||
// in the bundle path
|
// in the bundle path
|
||||||
func (c *Container) writeJSONFile(v interface{}, file string) (err error) {
|
func (c *Container) writeJSONFile(v interface{}, file string) error {
|
||||||
fileJSON, err := json.MarshalIndent(v, "", " ")
|
fileJSON, err := json.MarshalIndent(v, "", " ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "error writing JSON to %s for container %s", file, c.ID())
|
return errors.Wrapf(err, "error writing JSON to %s for container %s", file, c.ID())
|
||||||
|
|
@ -1967,7 +1967,7 @@ func (c *Container) writeJSONFile(v interface{}, file string) (err error) {
|
||||||
|
|
||||||
// prepareCheckpointExport writes the config and spec to
|
// prepareCheckpointExport writes the config and spec to
|
||||||
// JSON files for later export
|
// JSON files for later export
|
||||||
func (c *Container) prepareCheckpointExport() (err error) {
|
func (c *Container) prepareCheckpointExport() error {
|
||||||
// save live config
|
// save live config
|
||||||
if err := c.writeJSONFile(c.Config(), "config.dump"); err != nil {
|
if err := c.writeJSONFile(c.Config(), "config.dump"); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
||||||
|
|
@ -612,7 +612,7 @@ func (c *Container) addNamespaceContainer(g *generate.Generator, ns LinuxNS, ctr
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Container) exportCheckpoint(dest string, ignoreRootfs bool) (err error) {
|
func (c *Container) exportCheckpoint(dest string, ignoreRootfs bool) error {
|
||||||
if (len(c.config.NamedVolumes) > 0) || (len(c.Dependencies()) > 0) {
|
if (len(c.config.NamedVolumes) > 0) || (len(c.Dependencies()) > 0) {
|
||||||
return errors.Errorf("Cannot export checkpoints of containers with named volumes or dependencies")
|
return errors.Errorf("Cannot export checkpoints of containers with named volumes or dependencies")
|
||||||
}
|
}
|
||||||
|
|
@ -723,7 +723,7 @@ func (c *Container) exportCheckpoint(dest string, ignoreRootfs bool) (err error)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Container) checkpointRestoreSupported() (err error) {
|
func (c *Container) checkpointRestoreSupported() error {
|
||||||
if !criu.CheckForCriu() {
|
if !criu.CheckForCriu() {
|
||||||
return errors.Errorf("Checkpoint/Restore requires at least CRIU %d", criu.MinCriuVersion)
|
return errors.Errorf("Checkpoint/Restore requires at least CRIU %d", criu.MinCriuVersion)
|
||||||
}
|
}
|
||||||
|
|
@ -733,7 +733,7 @@ func (c *Container) checkpointRestoreSupported() (err error) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Container) checkpointRestoreLabelLog(fileName string) (err error) {
|
func (c *Container) checkpointRestoreLabelLog(fileName string) error {
|
||||||
// Create the CRIU log file and label it
|
// Create the CRIU log file and label it
|
||||||
dumpLog := filepath.Join(c.bundlePath(), fileName)
|
dumpLog := filepath.Join(c.bundlePath(), fileName)
|
||||||
|
|
||||||
|
|
@ -750,7 +750,7 @@ func (c *Container) checkpointRestoreLabelLog(fileName string) (err error) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointOptions) (err error) {
|
func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointOptions) error {
|
||||||
if err := c.checkpointRestoreSupported(); err != nil {
|
if err := c.checkpointRestoreSupported(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -820,7 +820,7 @@ func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointO
|
||||||
return c.save()
|
return c.save()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Container) importCheckpoint(input string) (err error) {
|
func (c *Container) importCheckpoint(input string) error {
|
||||||
archiveFile, err := os.Open(input)
|
archiveFile, err := os.Open(input)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "Failed to open checkpoint archive %s for import", input)
|
return errors.Wrapf(err, "Failed to open checkpoint archive %s for import", input)
|
||||||
|
|
@ -849,8 +849,7 @@ func (c *Container) importCheckpoint(input string) (err error) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Container) restore(ctx context.Context, options ContainerCheckpointOptions) (err error) {
|
func (c *Container) restore(ctx context.Context, options ContainerCheckpointOptions) (retErr error) {
|
||||||
|
|
||||||
if err := c.checkpointRestoreSupported(); err != nil {
|
if err := c.checkpointRestoreSupported(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -860,7 +859,7 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.TargetFile != "" {
|
if options.TargetFile != "" {
|
||||||
if err = c.importCheckpoint(options.TargetFile); err != nil {
|
if err := c.importCheckpoint(options.TargetFile); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -946,9 +945,9 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if retErr != nil {
|
||||||
if err2 := c.cleanup(ctx); err2 != nil {
|
if err := c.cleanup(ctx); err != nil {
|
||||||
logrus.Errorf("error cleaning up container %s: %v", c.ID(), err2)
|
logrus.Errorf("error cleaning up container %s: %v", c.ID(), err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ func (c *Container) unmountSHM(mount string) error {
|
||||||
return define.ErrNotImplemented
|
return define.ErrNotImplemented
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Container) prepare() (err error) {
|
func (c *Container) prepare() error {
|
||||||
return define.ErrNotImplemented
|
return define.ErrNotImplemented
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,20 +8,20 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewEventer creates an eventer based on the eventer type
|
// NewEventer creates an eventer based on the eventer type
|
||||||
func NewEventer(options EventerOptions) (eventer Eventer, err error) {
|
func NewEventer(options EventerOptions) (Eventer, error) {
|
||||||
logrus.Debugf("Initializing event backend %s", options.EventerType)
|
logrus.Debugf("Initializing event backend %s", options.EventerType)
|
||||||
switch strings.ToUpper(options.EventerType) {
|
switch strings.ToUpper(options.EventerType) {
|
||||||
case strings.ToUpper(Journald.String()):
|
case strings.ToUpper(Journald.String()):
|
||||||
eventer, err = newEventJournalD(options)
|
eventer, err := newEventJournalD(options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "eventer creation")
|
return nil, errors.Wrapf(err, "eventer creation")
|
||||||
}
|
}
|
||||||
|
return eventer, nil
|
||||||
case strings.ToUpper(LogFile.String()):
|
case strings.ToUpper(LogFile.String()):
|
||||||
eventer = EventLogFile{options}
|
return EventLogFile{options}, nil
|
||||||
case strings.ToUpper(Null.String()):
|
case strings.ToUpper(Null.String()):
|
||||||
eventer = NewNullEventer()
|
return NewNullEventer(), nil
|
||||||
default:
|
default:
|
||||||
return nil, errors.Errorf("unknown event logger type: %s", strings.ToUpper(options.EventerType))
|
return nil, errors.Errorf("unknown event logger type: %s", strings.ToUpper(options.EventerType))
|
||||||
}
|
}
|
||||||
return eventer, nil
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -141,18 +141,18 @@ func (r *Runtime) configureNetNS(ctr *Container, ctrNS ns.NetNS) ([]*cnitypes.Re
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create and configure a new network namespace for a container
|
// Create and configure a new network namespace for a container
|
||||||
func (r *Runtime) createNetNS(ctr *Container) (n ns.NetNS, q []*cnitypes.Result, err error) {
|
func (r *Runtime) createNetNS(ctr *Container) (n ns.NetNS, q []*cnitypes.Result, retErr error) {
|
||||||
ctrNS, err := netns.NewNS()
|
ctrNS, err := netns.NewNS()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, errors.Wrapf(err, "error creating network namespace for container %s", ctr.ID())
|
return nil, nil, errors.Wrapf(err, "error creating network namespace for container %s", ctr.ID())
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if retErr != nil {
|
||||||
if err2 := netns.UnmountNS(ctrNS); err2 != nil {
|
if err := netns.UnmountNS(ctrNS); err != nil {
|
||||||
logrus.Errorf("Error unmounting partially created network namespace for container %s: %v", ctr.ID(), err2)
|
logrus.Errorf("Error unmounting partially created network namespace for container %s: %v", ctr.ID(), err)
|
||||||
}
|
}
|
||||||
if err2 := ctrNS.Close(); err2 != nil {
|
if err := ctrNS.Close(); err != nil {
|
||||||
logrus.Errorf("Error closing partially created network namespace for container %s: %v", ctr.ID(), err2)
|
logrus.Errorf("Error closing partially created network namespace for container %s: %v", ctr.ID(), err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
@ -188,7 +188,7 @@ func checkSlirpFlags(path string) (*slirpFeatures, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure the network namespace for a rootless container
|
// Configure the network namespace for a rootless container
|
||||||
func (r *Runtime) setupRootlessNetNS(ctr *Container) (err error) {
|
func (r *Runtime) setupRootlessNetNS(ctr *Container) error {
|
||||||
path := r.config.Engine.NetworkCmdPath
|
path := r.config.Engine.NetworkCmdPath
|
||||||
|
|
||||||
if path == "" {
|
if path == "" {
|
||||||
|
|
@ -342,7 +342,7 @@ func waitForSync(syncR *os.File, cmd *exec.Cmd, logFile io.ReadSeeker, timeout t
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Runtime) setupRootlessPortMapping(ctr *Container, netnsPath string) (err error) {
|
func (r *Runtime) setupRootlessPortMapping(ctr *Container, netnsPath string) error {
|
||||||
syncR, syncW, err := os.Pipe()
|
syncR, syncW, err := os.Pipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "failed to open pipe")
|
return errors.Wrapf(err, "failed to open pipe")
|
||||||
|
|
@ -420,7 +420,7 @@ func (r *Runtime) setupRootlessPortMapping(ctr *Container, netnsPath string) (er
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure the network namespace using the container process
|
// Configure the network namespace using the container process
|
||||||
func (r *Runtime) setupNetNS(ctr *Container) (err error) {
|
func (r *Runtime) setupNetNS(ctr *Container) error {
|
||||||
nsProcess := fmt.Sprintf("/proc/%d/ns/net", ctr.state.PID)
|
nsProcess := fmt.Sprintf("/proc/%d/ns/net", ctr.state.PID)
|
||||||
|
|
||||||
b := make([]byte, 16)
|
b := make([]byte, 16)
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,11 @@ package libpod
|
||||||
|
|
||||||
import "github.com/containers/libpod/v2/libpod/define"
|
import "github.com/containers/libpod/v2/libpod/define"
|
||||||
|
|
||||||
func (r *Runtime) setupRootlessNetNS(ctr *Container) (err error) {
|
func (r *Runtime) setupRootlessNetNS(ctr *Container) error {
|
||||||
return define.ErrNotImplemented
|
return define.ErrNotImplemented
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Runtime) setupNetNS(ctr *Container) (err error) {
|
func (r *Runtime) setupNetNS(ctr *Container) error {
|
||||||
return define.ErrNotImplemented
|
return define.ErrNotImplemented
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -16,7 +16,7 @@ func (r *Runtime) teardownNetNS(ctr *Container) error {
|
||||||
return define.ErrNotImplemented
|
return define.ErrNotImplemented
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Runtime) createNetNS(ctr *Container) (err error) {
|
func (r *Runtime) createNetNS(ctr *Container) error {
|
||||||
return define.ErrNotImplemented
|
return define.ErrNotImplemented
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -191,7 +191,7 @@ func hasCurrentUserMapped(ctr *Container) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateContainer creates a container.
|
// CreateContainer creates a container.
|
||||||
func (r *ConmonOCIRuntime) CreateContainer(ctr *Container, restoreOptions *ContainerCheckpointOptions) (err error) {
|
func (r *ConmonOCIRuntime) CreateContainer(ctr *Container, restoreOptions *ContainerCheckpointOptions) error {
|
||||||
if !hasCurrentUserMapped(ctr) {
|
if !hasCurrentUserMapped(ctr) {
|
||||||
for _, i := range []string{ctr.state.RunDir, ctr.runtime.config.Engine.TmpDir, ctr.config.StaticDir, ctr.state.Mountpoint, ctr.runtime.config.Engine.VolumePath} {
|
for _, i := range []string{ctr.state.RunDir, ctr.runtime.config.Engine.TmpDir, ctr.config.StaticDir, ctr.state.Mountpoint, ctr.runtime.config.Engine.VolumePath} {
|
||||||
if err := makeAccessible(i, ctr.RootUID(), ctr.RootGID()); err != nil {
|
if err := makeAccessible(i, ctr.RootUID(), ctr.RootGID()); err != nil {
|
||||||
|
|
@ -853,7 +853,7 @@ func (r *ConmonOCIRuntime) getLogTag(ctr *Container) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// createOCIContainer generates this container's main conmon instance and prepares it for starting
|
// createOCIContainer generates this container's main conmon instance and prepares it for starting
|
||||||
func (r *ConmonOCIRuntime) createOCIContainer(ctr *Container, restoreOptions *ContainerCheckpointOptions) (err error) {
|
func (r *ConmonOCIRuntime) createOCIContainer(ctr *Container, restoreOptions *ContainerCheckpointOptions) error {
|
||||||
var stderrBuf bytes.Buffer
|
var stderrBuf bytes.Buffer
|
||||||
|
|
||||||
runtimeDir, err := util.GetRuntimeDir()
|
runtimeDir, err := util.GetRuntimeDir()
|
||||||
|
|
@ -1343,8 +1343,9 @@ func (r *ConmonOCIRuntime) moveConmonToCgroupAndSignal(ctr *Container, cmd *exec
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// newPipe creates a unix socket pair for communication
|
// newPipe creates a unix socket pair for communication.
|
||||||
func newPipe() (parent *os.File, child *os.File, err error) {
|
// Returns two files - first is parent, second is child.
|
||||||
|
func newPipe() (*os.File, *os.File, error) {
|
||||||
fds, err := unix.Socketpair(unix.AF_LOCAL, unix.SOCK_SEQPACKET|unix.SOCK_CLOEXEC, 0)
|
fds, err := unix.Socketpair(unix.AF_LOCAL, unix.SOCK_SEQPACKET|unix.SOCK_CLOEXEC, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
|
|
||||||
|
|
@ -126,7 +126,7 @@ func SetXdgDirs() error {
|
||||||
|
|
||||||
// NewRuntime creates a new container runtime
|
// NewRuntime creates a new container runtime
|
||||||
// Options can be passed to override the default configuration for the runtime
|
// Options can be passed to override the default configuration for the runtime
|
||||||
func NewRuntime(ctx context.Context, options ...RuntimeOption) (runtime *Runtime, err error) {
|
func NewRuntime(ctx context.Context, options ...RuntimeOption) (*Runtime, error) {
|
||||||
conf, err := config.NewConfig("")
|
conf, err := config.NewConfig("")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -140,13 +140,13 @@ func NewRuntime(ctx context.Context, options ...RuntimeOption) (runtime *Runtime
|
||||||
// functions can be used to mutate this configuration further.
|
// functions can be used to mutate this configuration further.
|
||||||
// An error will be returned if the configuration file at the given path does
|
// An error will be returned if the configuration file at the given path does
|
||||||
// not exist or cannot be loaded
|
// not exist or cannot be loaded
|
||||||
func NewRuntimeFromConfig(ctx context.Context, userConfig *config.Config, options ...RuntimeOption) (runtime *Runtime, err error) {
|
func NewRuntimeFromConfig(ctx context.Context, userConfig *config.Config, options ...RuntimeOption) (*Runtime, error) {
|
||||||
|
|
||||||
return newRuntimeFromConfig(ctx, userConfig, options...)
|
return newRuntimeFromConfig(ctx, userConfig, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newRuntimeFromConfig(ctx context.Context, conf *config.Config, options ...RuntimeOption) (runtime *Runtime, err error) {
|
func newRuntimeFromConfig(ctx context.Context, conf *config.Config, options ...RuntimeOption) (*Runtime, error) {
|
||||||
runtime = new(Runtime)
|
runtime := new(Runtime)
|
||||||
|
|
||||||
if conf.Engine.OCIRuntime == "" {
|
if conf.Engine.OCIRuntime == "" {
|
||||||
conf.Engine.OCIRuntime = "runc"
|
conf.Engine.OCIRuntime = "runc"
|
||||||
|
|
@ -236,7 +236,7 @@ func getLockManager(runtime *Runtime) (lock.Manager, error) {
|
||||||
|
|
||||||
// Make a new runtime based on the given configuration
|
// Make a new runtime based on the given configuration
|
||||||
// Sets up containers/storage, state store, OCI runtime
|
// Sets up containers/storage, state store, OCI runtime
|
||||||
func makeRuntime(ctx context.Context, runtime *Runtime) (err error) {
|
func makeRuntime(ctx context.Context, runtime *Runtime) (retErr error) {
|
||||||
// Find a working conmon binary
|
// Find a working conmon binary
|
||||||
cPath, err := runtime.config.FindConmon()
|
cPath, err := runtime.config.FindConmon()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -316,12 +316,11 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil && store != nil {
|
if retErr != nil && store != nil {
|
||||||
// Don't forcibly shut down
|
// Don't forcibly shut down
|
||||||
// We could be opening a store in use by another libpod
|
// We could be opening a store in use by another libpod
|
||||||
_, err2 := store.Shutdown(false)
|
if _, err := store.Shutdown(false); err != nil {
|
||||||
if err2 != nil {
|
logrus.Errorf("Error removing store for partially-created runtime: %s", err)
|
||||||
logrus.Errorf("Error removing store for partially-created runtime: %s", err2)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ type CtrCreateOption func(*Container) error
|
||||||
type ContainerFilter func(*Container) bool
|
type ContainerFilter func(*Container) bool
|
||||||
|
|
||||||
// NewContainer creates a new container from a given OCI config.
|
// NewContainer creates a new container from a given OCI config.
|
||||||
func (r *Runtime) NewContainer(ctx context.Context, rSpec *spec.Spec, options ...CtrCreateOption) (c *Container, err error) {
|
func (r *Runtime) NewContainer(ctx context.Context, rSpec *spec.Spec, options ...CtrCreateOption) (*Container, error) {
|
||||||
r.lock.Lock()
|
r.lock.Lock()
|
||||||
defer r.lock.Unlock()
|
defer r.lock.Unlock()
|
||||||
if !r.valid {
|
if !r.valid {
|
||||||
|
|
@ -44,7 +44,7 @@ func (r *Runtime) NewContainer(ctx context.Context, rSpec *spec.Spec, options ..
|
||||||
}
|
}
|
||||||
|
|
||||||
// RestoreContainer re-creates a container from an imported checkpoint
|
// RestoreContainer re-creates a container from an imported checkpoint
|
||||||
func (r *Runtime) RestoreContainer(ctx context.Context, rSpec *spec.Spec, config *ContainerConfig) (c *Container, err error) {
|
func (r *Runtime) RestoreContainer(ctx context.Context, rSpec *spec.Spec, config *ContainerConfig) (*Container, error) {
|
||||||
r.lock.Lock()
|
r.lock.Lock()
|
||||||
defer r.lock.Unlock()
|
defer r.lock.Unlock()
|
||||||
if !r.valid {
|
if !r.valid {
|
||||||
|
|
@ -68,7 +68,7 @@ func (r *Runtime) RestoreContainer(ctx context.Context, rSpec *spec.Spec, config
|
||||||
return r.setupContainer(ctx, ctr)
|
return r.setupContainer(ctx, ctr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Runtime) initContainerVariables(rSpec *spec.Spec, config *ContainerConfig) (c *Container, err error) {
|
func (r *Runtime) initContainerVariables(rSpec *spec.Spec, config *ContainerConfig) (*Container, error) {
|
||||||
if rSpec == nil {
|
if rSpec == nil {
|
||||||
return nil, errors.Wrapf(define.ErrInvalidArg, "must provide a valid runtime spec to create container")
|
return nil, errors.Wrapf(define.ErrInvalidArg, "must provide a valid runtime spec to create container")
|
||||||
}
|
}
|
||||||
|
|
@ -122,7 +122,7 @@ func (r *Runtime) initContainerVariables(rSpec *spec.Spec, config *ContainerConf
|
||||||
return ctr, nil
|
return ctr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Runtime) newContainer(ctx context.Context, rSpec *spec.Spec, options ...CtrCreateOption) (c *Container, err error) {
|
func (r *Runtime) newContainer(ctx context.Context, rSpec *spec.Spec, options ...CtrCreateOption) (*Container, error) {
|
||||||
span, _ := opentracing.StartSpanFromContext(ctx, "newContainer")
|
span, _ := opentracing.StartSpanFromContext(ctx, "newContainer")
|
||||||
span.SetTag("type", "runtime")
|
span.SetTag("type", "runtime")
|
||||||
defer span.Finish()
|
defer span.Finish()
|
||||||
|
|
@ -141,7 +141,7 @@ func (r *Runtime) newContainer(ctx context.Context, rSpec *spec.Spec, options ..
|
||||||
return r.setupContainer(ctx, ctr)
|
return r.setupContainer(ctx, ctr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Container, err error) {
|
func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Container, retErr error) {
|
||||||
// Validate the container
|
// Validate the container
|
||||||
if err := ctr.validate(); err != nil {
|
if err := ctr.validate(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -157,9 +157,9 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai
|
||||||
logrus.Debugf("Allocated lock %d for container %s", ctr.lock.ID(), ctr.ID())
|
logrus.Debugf("Allocated lock %d for container %s", ctr.lock.ID(), ctr.ID())
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if retErr != nil {
|
||||||
if err2 := ctr.lock.Free(); err2 != nil {
|
if err := ctr.lock.Free(); err != nil {
|
||||||
logrus.Errorf("Error freeing lock for container after creation failed: %v", err2)
|
logrus.Errorf("Error freeing lock for container after creation failed: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
@ -272,9 +272,9 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if retErr != nil {
|
||||||
if err2 := ctr.teardownStorage(); err2 != nil {
|
if err := ctr.teardownStorage(); err != nil {
|
||||||
logrus.Errorf("Error removing partially-created container root filesystem: %s", err2)
|
logrus.Errorf("Error removing partially-created container root filesystem: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
|
||||||
|
|
@ -34,13 +34,13 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Get an empty BoltDB state for use in tests
|
// Get an empty BoltDB state for use in tests
|
||||||
func getEmptyBoltState() (s State, p string, m lock.Manager, err error) {
|
func getEmptyBoltState() (_ State, _ string, _ lock.Manager, retErr error) {
|
||||||
tmpDir, err := ioutil.TempDir("", tmpDirPrefix)
|
tmpDir, err := ioutil.TempDir("", tmpDirPrefix)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", nil, err
|
return nil, "", nil, err
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if retErr != nil {
|
||||||
os.RemoveAll(tmpDir)
|
os.RemoveAll(tmpDir)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
@ -66,13 +66,13 @@ func getEmptyBoltState() (s State, p string, m lock.Manager, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get an empty in-memory state for use in tests
|
// Get an empty in-memory state for use in tests
|
||||||
func getEmptyInMemoryState() (s State, p string, m lock.Manager, err error) {
|
func getEmptyInMemoryState() (_ State, _ string, _ lock.Manager, retErr error) {
|
||||||
tmpDir, err := ioutil.TempDir("", tmpDirPrefix)
|
tmpDir, err := ioutil.TempDir("", tmpDirPrefix)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", nil, err
|
return nil, "", nil, err
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if retErr != nil {
|
||||||
os.RemoveAll(tmpDir)
|
os.RemoveAll(tmpDir)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ func (metadata *RuntimeContainerMetadata) SetMountLabel(mountLabel string) {
|
||||||
|
|
||||||
// CreateContainerStorage creates the storage end of things. We already have the container spec created
|
// CreateContainerStorage creates the storage end of things. We already have the container spec created
|
||||||
// TO-DO We should be passing in an Image object in the future.
|
// TO-DO We should be passing in an Image object in the future.
|
||||||
func (r *storageService) CreateContainerStorage(ctx context.Context, systemContext *types.SystemContext, imageName, imageID, containerName, containerID string, options storage.ContainerOptions) (cinfo ContainerInfo, err error) {
|
func (r *storageService) CreateContainerStorage(ctx context.Context, systemContext *types.SystemContext, imageName, imageID, containerName, containerID string, options storage.ContainerOptions) (_ ContainerInfo, retErr error) {
|
||||||
span, _ := opentracing.StartSpanFromContext(ctx, "createContainerStorage")
|
span, _ := opentracing.StartSpanFromContext(ctx, "createContainerStorage")
|
||||||
span.SetTag("type", "storageService")
|
span.SetTag("type", "storageService")
|
||||||
defer span.Finish()
|
defer span.Finish()
|
||||||
|
|
@ -132,9 +132,9 @@ func (r *storageService) CreateContainerStorage(ctx context.Context, systemConte
|
||||||
// If anything fails after this point, we need to delete the incomplete
|
// If anything fails after this point, we need to delete the incomplete
|
||||||
// container before returning.
|
// container before returning.
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if retErr != nil {
|
||||||
if err2 := r.store.DeleteContainer(container.ID); err2 != nil {
|
if err := r.store.DeleteContainer(container.ID); err != nil {
|
||||||
logrus.Infof("%v deleting partially-created container %q", err2, container.ID)
|
logrus.Infof("%v deleting partially-created container %q", err, container.ID)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue