Skip to content

Commit

Permalink
Merge pull request #5838 from oasisprotocol/kostko/feature/bundle-noelf
Browse files Browse the repository at this point in the history
go/runtime: Support bundle components without ELF binary
  • Loading branch information
kostko authored Sep 4, 2024
2 parents 050a01f + 7048cbb commit bfa8e50
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 46 deletions.
1 change: 1 addition & 0 deletions .changelog/5838.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
go/runtime: Support bundle components without ELF binary
24 changes: 17 additions & 7 deletions go/runtime/bundle/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ func (m *Manifest) Hash() hash.Hash {
return hash.NewFrom(m)
}

// IsLegacy returns true iff this is a legacy manifest that defines executables at the top level.
func (m *Manifest) IsLegacy() bool {
return len(m.Executable) > 0 || m.SGX != nil
}

// Validate validates the manifest structure for well-formedness.
func (m *Manifest) Validate() error {
byID := make(map[component.ID]struct{})
Expand All @@ -62,7 +67,7 @@ func (m *Manifest) Validate() error {
}
}

if _, ok := byID[component.ID_RONL]; ok && len(m.Executable) > 0 {
if _, ok := byID[component.ID_RONL]; ok && m.IsLegacy() {
return fmt.Errorf("manifest defines both legacy and componentized RONL component")
}

Expand Down Expand Up @@ -105,7 +110,7 @@ func (m *Manifest) GetComponentByID(id component.ID) *Component {
}

// We also support legacy manifests which define the RONL component at the top-level.
if id.IsRONL() && len(m.Executable) > 0 {
if id.IsRONL() && m.IsLegacy() {
return &Component{
Kind: component.RONL,
Executable: m.Executable,
Expand Down Expand Up @@ -141,8 +146,8 @@ type Component struct {
// provided by a runtime.
Name string `json:"name,omitempty"`

// Executable is the name of the runtime ELF executable file.
Executable string `json:"executable"`
// Executable is the name of the runtime ELF executable file if any.
Executable string `json:"executable,omitempty"`

// SGX is the SGX specific manifest metadata if any.
SGX *SGXMetadata `json:"sgx,omitempty"`
Expand All @@ -164,9 +169,6 @@ func (c *Component) Matches(id component.ID) bool {

// Validate validates the component structure for well-formedness.
func (c *Component) Validate() error {
if c.Executable == "" {
return fmt.Errorf("executable must be set")
}
if c.SGX != nil {
err := c.SGX.Validate()
if err != nil {
Expand All @@ -179,6 +181,9 @@ func (c *Component) Validate() error {
if c.Name != "" {
return fmt.Errorf("RONL component must have an empty name")
}
if c.Executable == "" {
return fmt.Errorf("RONL component must define an executable")
}
if c.Disabled {
return fmt.Errorf("RONL component cannot be disabled")
}
Expand All @@ -200,3 +205,8 @@ func (c *Component) IsNetworkAllowed() bool {
return false
}
}

// IsTEERequired returns true iff the component only provides TEE executables.
func (c *Component) IsTEERequired() bool {
return c.Executable == "" && c.SGX != nil
}
94 changes: 55 additions & 39 deletions go/runtime/registry/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,62 @@ func newConfig( //nolint: gocyclo

// Check if any runtimes are configured to be hosted.
if haveSetRuntimes || (cmdFlags.DebugDontBlameOasis() && viper.IsSet(CfgDebugMockIDs)) {
// By default start with the environment specified in configuration.
runtimeEnv := config.GlobalConfig.Runtime.Environment

// Preprocess runtimes to separate detached from non-detached.
type nameKey struct {
runtime common.Namespace
comp component.ID
}

var (
regularBundles []*bundle.Bundle
err error
)
detachedBundles := make(map[common.Namespace][]*bundle.Bundle)
existingNames := make(map[nameKey]struct{})
for _, path := range config.GlobalConfig.Runtime.Paths {
var bnd *bundle.Bundle
if bnd, err = bundle.Open(path); err != nil {
return nil, fmt.Errorf("failed to load runtime bundle '%s': %w", path, err)
}
if err = bnd.WriteExploded(dataDir); err != nil {
return nil, fmt.Errorf("failed to explode runtime bundle '%s': %w", path, err)
}
// Release resources as the bundle has been exploded anyway.
bnd.Data = nil

switch bnd.Manifest.IsDetached() {
case false:
// A regular non-detached bundle that has the RONL component.
regularBundles = append(regularBundles, bnd)
case true:
// A detached bundle without the RONL component that needs to be attached.
detachedBundles[bnd.Manifest.ID] = append(detachedBundles[bnd.Manifest.ID], bnd)

// Ensure there are no name conflicts among the components.
for compID := range bnd.Manifest.GetAvailableComponents() {
nk := nameKey{bnd.Manifest.ID, compID}
if _, ok := existingNames[nk]; ok {
return nil, fmt.Errorf("duplicate component '%s' for runtime '%s'", compID, bnd.Manifest.ID)
}
existingNames[nk] = struct{}{}
}
}

// If the runtime environment is set to automatic selection and a bundle has a component
// that requires the use of a TEE, force a TEE environment to simplify configuration.
if runtimeEnv == rtConfig.RuntimeEnvironmentAuto {
for _, comp := range bnd.Manifest.GetAvailableComponents() {
if comp.IsTEERequired() {
runtimeEnv = rtConfig.RuntimeEnvironmentSGX
break
}
}
}
}

isEnvSGX := runtimeEnv == rtConfig.RuntimeEnvironmentSGX || runtimeEnv == rtConfig.RuntimeEnvironmentSGXMock
forceNoSGX := (config.GlobalConfig.Mode.IsClientOnly() && !isEnvSGX) ||
(cmdFlags.DebugDontBlameOasis() && runtimeEnv == rtConfig.RuntimeEnvironmentELF)
Expand Down Expand Up @@ -226,45 +281,6 @@ func newConfig( //nolint: gocyclo
})
}

// Preprocess runtimes to separate detached from non-detached.
type nameKey struct {
runtime common.Namespace
comp component.ID
}

var regularBundles []*bundle.Bundle
detachedBundles := make(map[common.Namespace][]*bundle.Bundle)
existingNames := make(map[nameKey]struct{})
for _, path := range config.GlobalConfig.Runtime.Paths {
var bnd *bundle.Bundle
if bnd, err = bundle.Open(path); err != nil {
return nil, fmt.Errorf("failed to load runtime bundle '%s': %w", path, err)
}
if err = bnd.WriteExploded(dataDir); err != nil {
return nil, fmt.Errorf("failed to explode runtime bundle '%s': %w", path, err)
}
// Release resources as the bundle has been exploded anyway.
bnd.Data = nil

switch bnd.Manifest.IsDetached() {
case false:
// A regular non-detached bundle that has the RONL component.
regularBundles = append(regularBundles, bnd)
case true:
// A detached bundle without the RONL component that needs to be attached.
detachedBundles[bnd.Manifest.ID] = append(detachedBundles[bnd.Manifest.ID], bnd)

// Ensure there are no name conflicts among the components.
for compID := range bnd.Manifest.GetAvailableComponents() {
nk := nameKey{bnd.Manifest.ID, compID}
if _, ok := existingNames[nk]; ok {
return nil, fmt.Errorf("duplicate component '%s' for runtime '%s'", compID, bnd.Manifest.ID)
}
existingNames[nk] = struct{}{}
}
}
}

// Configure runtimes.
rh.Runtimes = make(map[common.Namespace]map[version.Version]*runtimeHost.Config)
for _, bnd := range regularBundles {
Expand Down

0 comments on commit bfa8e50

Please sign in to comment.