Skip to content

Allow setting architecture of containers #771

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions ENVIRONMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,15 @@ If 1, always prints the Homeserver container logs even on success. When used wit
This allows you to override the base image used for a particular named homeserver. For example, `COMPLEMENT_BASE_IMAGE_HS1=complement-dendrite:latest` would use `complement-dendrite:latest` for the `hs1` homeserver in blueprints, but not any other homeserver (e.g `hs2`). This matching is case-insensitive. This allows Complement to test how different homeserver implementations work with each other.
- Type: `map[string]string`

#### `COMPLEMENT_BASE_ARCH`
This allows you to override the architecture of the Docker image to use as a base homeserver when generating blueprints. This can be used to emulate a particular architecture with a multi-platform image. If "", the architecture of the host running Complement is used.
- Type: `string`
- Default: ""

#### `COMPLEMENT_BASE_ARCH_*`
This allows you to set the architecture of the base image used for a particular named homeserver. For example, `COMPLEMENT_BASE_ARCH_HS1=arm64` would use an `arm64` for the `hs1` homeserver in blueprints, but not any other homeserver (e.g `hs2`). This matching is case-insensitive.
- Type: `map[string]string`

#### `COMPLEMENT_DEBUG`
If 1, prints out more verbose logging such as HTTP request/response bodies.
- Type: `bool`
Expand Down
2 changes: 2 additions & 0 deletions b/blueprints.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ type Homeserver struct {
ApplicationServices []ApplicationService
// Optionally override the baseImageURI for blueprint creation
BaseImageURI *string
// Optionally override the baseImageArch for blueprint creation
BaseImageArch *string
}

type User struct {
Expand Down
26 changes: 23 additions & 3 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ type Complement struct {
// blueprints. This image must conform to Complement's rules on containers, such as listening on the
// correct ports.
BaseImageURI string
// Name: COMPLEMENT_BASE_ARCH
// Default: ""
// Description: The architecture of the Docker image to use as a base homeserver when generating
// blueprints. This can be used to emulate a particular architecture with a multi-platform image.
// If "", the architecture of the host running Complement is used.
BaseImageArch string
// Name: COMPLEMENT_DEBUG
// Default: 0
// Description: If 1, prints out more verbose logging such as HTTP request/response bodies.
Expand Down Expand Up @@ -71,6 +77,11 @@ type Complement struct {
// for the `hs1` homeserver in blueprints, but not any other homeserver (e.g `hs2`). This matching
// is case-insensitive. This allows Complement to test how different homeserver implementations work with each other.
BaseImageURIs map[string]string
// Name: COMPLEMENT_BASE_ARCH_*
// Description: This allows you to set the architecture of the base image used for a particular named homeserver.
// For example, `COMPLEMENT_BASE_ARCH_HS1=arm64` would use an `arm64` image for the `hs1` homeserver in blueprints,
// but not any other homeserver (e.g `hs2`). This matching is case-insensitive.
BaseImageArchs map[string]string

// The namespace for all complement created blueprints and deployments
PackageNamespace string
Expand Down Expand Up @@ -118,14 +129,19 @@ type Complement struct {
PostTestScript string
}

var hsRegex = regexp.MustCompile(`COMPLEMENT_BASE_IMAGE_(.+)=(.+)$`)
var hsUriRegex = regexp.MustCompile(`COMPLEMENT_BASE_IMAGE_(.+)=(.+)$`)
var hsArchRegex = regexp.MustCompile(`COMPLEMENT_BASE_ARCH_(.+)=(.+)$`)

func NewConfigFromEnvVars(pkgNamespace, baseImageURI string) *Complement {
cfg := &Complement{BaseImageURIs: map[string]string{}}
cfg := &Complement{
BaseImageURIs: map[string]string{},
BaseImageArchs: map[string]string{},
}
cfg.BaseImageURI = os.Getenv("COMPLEMENT_BASE_IMAGE")
if cfg.BaseImageURI == "" {
cfg.BaseImageURI = baseImageURI
}
cfg.BaseImageArch = os.Getenv("COMPLEMENT_BASE_ARCH")
cfg.DebugLoggingEnabled = os.Getenv("COMPLEMENT_DEBUG") == "1"
cfg.AlwaysPrintServerLogs = os.Getenv("COMPLEMENT_ALWAYS_PRINT_SERVER_LOGS") == "1"
cfg.EnableDirtyRuns = os.Getenv("COMPLEMENT_ENABLE_DIRTY_RUNS") == "1"
Expand Down Expand Up @@ -153,10 +169,14 @@ func NewConfigFromEnvVars(pkgNamespace, baseImageURI string) *Complement {
for _, env := range os.Environ() {
// FindStringSubmatch returns the complete match as well as the capture groups.
// In this case we expect there to be 3 matches.
if matches := hsRegex.FindStringSubmatch(env); len(matches) == 3 {
if matches := hsUriRegex.FindStringSubmatch(env); len(matches) == 3 {
hs := matches[1] // first capture group; homeserver name
cfg.BaseImageURIs[hs] = matches[2] // second capture group; homeserver image
}
if matches := hsArchRegex.FindStringSubmatch(env); len(matches) == 3 {
hs := matches[1] // first capture group; homeserver name
cfg.BaseImageArchs[hs] = matches[2] // second capture group; homeserver arch
}
}

cfg.PackageNamespace = pkgNamespace
Expand Down
7 changes: 6 additions & 1 deletion internal/docker/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -398,18 +398,23 @@ func (d *Builder) constructHomeserver(blueprintName string, runner *instruction.
func (d *Builder) deployBaseImage(blueprintName string, hs b.Homeserver, contextStr, networkName string) (*HomeserverDeployment, error) {
asIDToRegistrationMap := asIDToRegistrationFromLabels(labelsForApplicationServices(hs))
var baseImageURI string
var baseImageArch string
if hs.BaseImageURI == nil {
baseImageURI = d.Config.BaseImageURI
// Use HS specific base image if defined
if uri, ok := d.Config.BaseImageURIs[hs.Name]; ok {
baseImageURI = uri
}
if arch, ok := d.Config.BaseImageArchs[hs.Name]; ok {
baseImageArch = arch
}
} else {
baseImageURI = *hs.BaseImageURI
baseImageArch = *hs.BaseImageArch
}

return deployImage(
d.Docker, baseImageURI, fmt.Sprintf("complement_%s", contextStr),
d.Docker, baseImageURI, baseImageArch, fmt.Sprintf("complement_%s", contextStr),
d.Config.PackageNamespace, blueprintName, hs.Name, asIDToRegistrationMap, contextStr,
networkName, d.Config,
)
Expand Down
16 changes: 12 additions & 4 deletions internal/docker/deployer.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import (
"github.com/docker/docker/api/types/image"
"github.com/docker/docker/api/types/mount"
"github.com/docker/docker/api/types/network"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"

"github.com/matrix-org/complement/config"
)
Expand Down Expand Up @@ -90,10 +91,14 @@ func (d *Deployer) CreateDirtyServer(hsName string) (*HomeserverDeployment, erro
if uri, ok := d.config.BaseImageURIs[hsName]; ok {
baseImageURI = uri
}
baseImageArch := d.config.BaseImageArch
if arch, ok := d.config.BaseImageArchs[hsName]; ok {
baseImageArch = arch
}

containerName := fmt.Sprintf("complement_%s_dirty_%s", d.config.PackageNamespace, hsName)
hsDeployment, err := deployImage(
d.Docker, baseImageURI, containerName,
d.Docker, baseImageURI, baseImageArch, containerName,
d.config.PackageNamespace, "", hsName, nil, "dirty",
networkName, d.config,
)
Expand Down Expand Up @@ -172,7 +177,7 @@ func (d *Deployer) Deploy(ctx context.Context, blueprintName string) (*Deploymen
// TODO: Make CSAPI port configurable
containerName := fmt.Sprintf("complement_%s_%s_%s_%d", d.config.PackageNamespace, d.DeployNamespace, contextStr, counter)
deployment, err := deployImage(
d.Docker, img.ID, containerName,
d.Docker, img.ID, "", containerName,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is an image ID tied to a particular architecture? If not, the correct arch has to be looked up here (and must consider the overrides per homeserver name).

d.config.PackageNamespace, blueprintName, hsName, asIDToRegistrationMap, contextStr, networkName, d.config,
)
if err != nil {
Expand Down Expand Up @@ -329,7 +334,7 @@ func (d *Deployer) StartServer(hsDep *HomeserverDeployment) error {

// nolint
func deployImage(
docker *client.Client, imageID string, containerName, pkgNamespace, blueprintName, hsName string,
docker *client.Client, imageID, imageArch, containerName, pkgNamespace, blueprintName, hsName string,
asIDToRegistrationMap map[string]string, contextStr, networkName string, cfg *config.Complement,
) (*HomeserverDeployment, error) {
ctx := context.Background()
Expand Down Expand Up @@ -402,7 +407,10 @@ func deployImage(
Aliases: []string{hsName},
},
},
}, nil, containerName)
}, &ocispec.Platform{
OS: "linux",
Architecture: imageArch,
Comment on lines +411 to +412
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is hardcoding the OS here too rigid? Alternatively, the COMPLEMENT_BASE_ARCH could be BASE_PLATFORM and be a full <os>/<arch> string.

}, containerName)
if err != nil {
return nil, err
}
Expand Down
Loading