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 }