github.com/apptainer/singularity@v3.1.1+incompatible/internal/pkg/runtime/engines/engines.go (about)

     1  // Copyright (c) 2019, Sylabs Inc. All rights reserved.
     2  // This software is licensed under a 3-clause BSD license. Please consult the
     3  // LICENSE.md file distributed with the sources of this project regarding your
     4  // rights to use or distribute this software.
     5  
     6  package engines
     7  
     8  import (
     9  	"encoding/json"
    10  	"fmt"
    11  	"net"
    12  	"net/rpc"
    13  	"os"
    14  	"syscall"
    15  
    16  	"github.com/sylabs/singularity/internal/pkg/runtime/engines/config"
    17  	"github.com/sylabs/singularity/internal/pkg/runtime/engines/config/starter"
    18  	"github.com/sylabs/singularity/internal/pkg/runtime/engines/imgbuild"
    19  	imgbuildConfig "github.com/sylabs/singularity/internal/pkg/runtime/engines/imgbuild/config"
    20  	"github.com/sylabs/singularity/internal/pkg/runtime/engines/oci"
    21  	ociserver "github.com/sylabs/singularity/internal/pkg/runtime/engines/oci/rpc/server"
    22  	"github.com/sylabs/singularity/internal/pkg/runtime/engines/singularity"
    23  	singularityConfig "github.com/sylabs/singularity/internal/pkg/runtime/engines/singularity/config"
    24  	"github.com/sylabs/singularity/internal/pkg/runtime/engines/singularity/rpc/server"
    25  )
    26  
    27  // Engine is the combination of an EngineOperations and a config.Common. The singularity
    28  // startup routines (src/runtime/startup/*) can spawn a container process from this type
    29  type Engine struct {
    30  	EngineOperations
    31  	*config.Common
    32  }
    33  
    34  // EngineOperations is an interface describing necessary operations to launch a container process.
    35  type EngineOperations interface {
    36  	// Config returns the current EngineConfig, used to populate the Common struct
    37  	Config() config.EngineConfig
    38  	// InitConfig is responsible for storing the parse config.Common inside
    39  	// the EngineOperations implementation.
    40  	InitConfig(*config.Common)
    41  	// PrepareConfig is called in stage1 to validate and prepare container configuration.
    42  	PrepareConfig(*starter.Config) error
    43  	// CreateContainer is called in master and does mount operations, etc... to
    44  	// set up the container environment for the payload proc.
    45  	CreateContainer(int, net.Conn) error
    46  	// StartProcess is called in stage2 after waiting on RPC server exit. It is
    47  	// responsible for exec'ing the payload proc in the container.
    48  	StartProcess(net.Conn) error
    49  	// PostStartProcess is called in master after successful execution of container process.
    50  	PostStartProcess(int) error
    51  	// MonitorContainer is called in master once the container proc has been spawned. It
    52  	// will typically block until the container proc exists.
    53  	MonitorContainer(int, chan os.Signal) (syscall.WaitStatus, error)
    54  	// CleanupContainer is called in master after the MontiorContainer returns. It is responsible
    55  	// for ensuring that the container has been properly torn down.
    56  	CleanupContainer(error, syscall.WaitStatus) error
    57  }
    58  
    59  // GetName returns the engine name set in JSON []byte configuration.
    60  func GetName(b []byte) string {
    61  	engineName := struct {
    62  		EngineName string `json:"engineName"`
    63  	}{}
    64  	if err := json.Unmarshal(b, &engineName); err != nil {
    65  		return ""
    66  	}
    67  	return engineName.EngineName
    68  }
    69  
    70  // NewEngine returns the engine described by the JSON []byte configuration.
    71  func NewEngine(b []byte) (*Engine, error) {
    72  	engineName := GetName(b)
    73  
    74  	// ensure engine with given name is registered
    75  	eOp, ok := registeredEngineOperations[engineName]
    76  	if !ok {
    77  		return nil, fmt.Errorf("engine %q is not found", engineName)
    78  	}
    79  
    80  	// create empty Engine object with properly initialized EngineConfig && EngineOperations
    81  	e := &Engine{
    82  		EngineOperations: eOp,
    83  		Common: &config.Common{
    84  			EngineConfig: eOp.Config(),
    85  		},
    86  	}
    87  
    88  	// parse received JSON configuration to specific EngineConfig
    89  	if err := json.Unmarshal(b, e.Common); err != nil {
    90  		return nil, fmt.Errorf("could not parse JSON configuration: %s", err)
    91  	}
    92  	e.InitConfig(e.Common)
    93  	return e, nil
    94  }
    95  
    96  var (
    97  	registeredEngineOperations map[string]EngineOperations
    98  
    99  	// registerEngineRPCMethods contains a map relating an Engine name to a set
   100  	// of RPC methods served by RPC server
   101  	registeredEngineRPCMethods map[string]interface{}
   102  )
   103  
   104  // ServeRuntimeEngineRequests serves runtime engine requests with corresponding registered engine methods.
   105  func ServeRuntimeEngineRequests(name string, conn net.Conn) {
   106  	methods := registeredEngineRPCMethods[name]
   107  	rpc.RegisterName(name, methods)
   108  	rpc.ServeConn(conn)
   109  }
   110  
   111  // Init initializes registered runtime engines
   112  func Init() {
   113  	registeredEngineOperations = make(map[string]EngineOperations)
   114  	registeredEngineOperations[singularityConfig.Name] = &singularity.EngineOperations{EngineConfig: singularityConfig.NewConfig()}
   115  	registeredEngineOperations[imgbuildConfig.Name] = &imgbuild.EngineOperations{EngineConfig: &imgbuildConfig.EngineConfig{}}
   116  	registeredEngineOperations[oci.Name] = &oci.EngineOperations{EngineConfig: &oci.EngineConfig{}}
   117  
   118  	// register singularity rpc methods
   119  	methods := new(server.Methods)
   120  	registeredEngineRPCMethods = make(map[string]interface{})
   121  	registeredEngineRPCMethods[singularityConfig.Name] = methods
   122  	registeredEngineRPCMethods[imgbuildConfig.Name] = methods
   123  
   124  	ocimethods := new(ociserver.Methods)
   125  	ocimethods.Methods = methods
   126  	registeredEngineRPCMethods[oci.Name] = ocimethods
   127  }