package osbuild2
import (
"bytes"
"encoding/json"
"fmt"
"regexp"
"sort"
)
type OCIArchiveStageOptions struct {
// The CPU architecture of the image
Architecture string `json:"architecture"`
// Resulting image filename
Filename string `json:"filename"`
// The execution parameters
Config *OCIArchiveConfig `json:"config,omitempty"`
}
type OCIArchiveConfig struct {
Cmd []string `json:"Cmd,omitempty"`
Env []string `json:"Env,omitempty"`
ExposedPorts []string `json:"ExposedPorts,omitempty"`
User string `json:"User,omitempty"`
Labels map[string]string `json:"Labels,omitempty"`
StopSignal string `json:"StopSignal,omitempty"`
Volumes []string `json:"Volumes,omitempty"`
WorkingDir string `json:"WorkingDir,omitempty"`
}
func (OCIArchiveStageOptions) isStageOptions() {}
type OCIArchiveStageInputs struct {
// Base layer for the container
Base *OCIArchiveStageInput `json:"base"`
// Additional layers in ascending order
Layers []OCIArchiveStageInput `json:",omitempty"`
}
func (OCIArchiveStageInputs) isStageInputs() {}
type OCIArchiveStageInput struct {
inputCommon
References OCIArchiveStageReferences `json:"references"`
}
func (OCIArchiveStageInput) isStageInput() {}
type OCIArchiveStageReferences []string
func (OCIArchiveStageReferences) isReferences() {}
// A new OCIArchiveStage to to assemble an OCI image archive
func NewOCIArchiveStage(options *OCIArchiveStageOptions, inputs *OCIArchiveStageInputs) *Stage {
return &Stage{
Type: "org.osbuild.oci-archive",
Options: options,
Inputs: inputs,
}
}
// Custom marshaller for OCIArchiveStageInputs, needed to generate keys of the
// form "layer.N", (where N = 1, 2, ...) for the Layers property
func (inputs *OCIArchiveStageInputs) MarshalJSON() ([]byte, error) {
if inputs == nil {
return json.Marshal(inputs)
}
layers := inputs.Layers
inputsMap := make(map[string]OCIArchiveStageInput, len(layers)+1)
if inputs.Base != nil {
inputsMap["base"] = *inputs.Base
}
for idx, input := range layers {
key := fmt.Sprintf("layer.%d", idx+1)
inputsMap[key] = input
}
return json.Marshal(inputsMap)
}
// Get the sorted keys that match the pattern "layer.N" (for N > 0)
func layerKeys(layers map[string]OCIArchiveStageInput) ([]string, error) {
keys := make([]string, 0, len(layers))
for key := range layers {
re := regexp.MustCompile(`layer\.[1-9]\d*`)
if key == "base" {
continue
}
if !re.MatchString(key) {
return nil, fmt.Errorf("invalid key: %q", key)
}
keys = append(keys, key)
}
sort.Strings(keys)
return keys, nil
}
// Custom unmarshaller for OCIArchiveStageInputs, needed to handle keys of the
// form "layer.N", (where N = 1, 2, ...) for the Layers property
func (inputs *OCIArchiveStageInputs) UnmarshalJSON(data []byte) error {
if len(data) == 0 {
return nil
}
if inputs == nil {
inputs = new(OCIArchiveStageInputs)
}
inputsMap := make(map[string]OCIArchiveStageInput)
dec := json.NewDecoder(bytes.NewReader(data))
dec.DisallowUnknownFields()
if err := dec.Decode(&inputsMap); err != nil {
return err
}
// "base" layer is required
base, ok := inputsMap["base"]
if !ok {
return fmt.Errorf("missing required key \"base\"")
}
inputs.Base = &base
keys, err := layerKeys(inputsMap)
if err != nil {
return err
}
inputs.Layers = make([]OCIArchiveStageInput, len(inputsMap)-1)
for idx, key := range keys {
inputs.Layers[idx] = inputsMap[key]
}
return nil
}