From afd73a044f9d48a72c7bd37ac3ee6c882ba75066 Mon Sep 17 00:00:00 2001 From: mac641 Date: Tue, 18 Nov 2025 15:35:20 +0100 Subject: [PATCH 1/6] feat(api): add oci config --- api/api.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/api/api.go b/api/api.go index d7ccc95..097c060 100644 --- a/api/api.go +++ b/api/api.go @@ -13,7 +13,8 @@ type MetalConfig struct { NTPServers []string `json:"ntp_servers,omitempty"` Partition string `json:"partition"` // Logging contains logging configurations passed to metal-hammer - Logging *Logging `json:"logging,omitempty"` + Logging *Logging `json:"logging,omitempty"` + OciConfig *OciConfig `json:"oci_config,omitempty"` } type Logging struct { @@ -45,6 +46,15 @@ type CertificateAuth struct { InsecureSkipVerify bool `json:"insecure_skip_verify,omitempty"` } +type OciConfig struct { + // URL pointing to the oci registry + RegistryURL string `json:"registry_url"` + // Username that is capable of logging in to the registry + Username string `json:"username,omitempty"` + // Password for the user + Password string `json:"password,omitempty"` +} + // LogType defines which logging backend should be used type LogType string From 0cd55ab0ff7a0ad43958a710131e2d90dde8e23b Mon Sep 17 00:00:00 2001 From: mac641 Date: Wed, 19 Nov 2025 09:16:48 +0100 Subject: [PATCH 2/6] feat(api): allow multiple oci configs in case multiple images use different registries --- api/api.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/api.go b/api/api.go index 097c060..201c1d9 100644 --- a/api/api.go +++ b/api/api.go @@ -13,8 +13,8 @@ type MetalConfig struct { NTPServers []string `json:"ntp_servers,omitempty"` Partition string `json:"partition"` // Logging contains logging configurations passed to metal-hammer - Logging *Logging `json:"logging,omitempty"` - OciConfig *OciConfig `json:"oci_config,omitempty"` + Logging *Logging `json:"logging,omitempty"` + OciConfig []*OciConfig `json:"oci_config,omitempty"` } type Logging struct { From a5e53cfa2837885f7c683cb4471998befc48aadb Mon Sep 17 00:00:00 2001 From: mac641 Date: Thu, 20 Nov 2025 16:14:33 +0100 Subject: [PATCH 3/6] fix(cli): wire oci-configs to grpccmd as requested by @majst01 --- pixiecore/cli/grpccmd.go | 41 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/pixiecore/cli/grpccmd.go b/pixiecore/cli/grpccmd.go index 8d36001..a050f8f 100644 --- a/pixiecore/cli/grpccmd.go +++ b/pixiecore/cli/grpccmd.go @@ -85,6 +85,9 @@ func init() { grpcCmd.Flags().String("metal-hammer-logging-key", "", "set metal-hammer to send logs to a remote endpoint and authenticate with this key") grpcCmd.Flags().Bool("metal-hammer-logging-tls-insecure", false, "set metal-hammer to send logs to a remote endpoint without verifying the tls certificate") grpcCmd.Flags().String("metal-hammer-logging-type", "loki", "set metal-hammer to send logs to a remote endpoint with this logging type") + + // metal-hammer oci configs + grpcCmd.Flags().StringSlice("metal-hammer-oci-configs", nil, "multiple metal-hammer oci configs. comma-separated key-value pairs (registry_url=...,username=...,password=...). registry URL is mandatory, login credentials are optional depending on whether the oci image is public.") } func getMetalAPIConfig(cmd *cobra.Command) (*api.MetalConfig, error) { @@ -219,6 +222,43 @@ func getMetalAPIConfig(cmd *cobra.Command) (*api.MetalConfig, error) { } } + metalHammerOciConfigs, err := cmd.Flags().GetStringSlice("metal-hammer-oci-configs") + if err != nil { + return nil, fmt.Errorf("error reading flag: %w", err) + } + + var ociConfigs []*api.OciConfig + + for _, c := range metalHammerOciConfigs { + var ociConfig *api.OciConfig + + parts := strings.SplitSeq(c, ",") + for p := range parts { + kv := strings.SplitN(strings.TrimSpace(p), "=", 2) + if len(kv) != 2 { + return nil, fmt.Errorf("invalid key-value pair in OCI config: %q", p) + } + + k := strings.ToLower(strings.TrimSpace(kv[0])) + v := strings.TrimSpace(kv[1]) + switch k { + case "registry_url": + if v == "" { + return nil, fmt.Errorf("no registry url specified for oci config: %s", c) + } + ociConfig.RegistryURL = v + case "username": + ociConfig.Username = v + case "password": + ociConfig.Password = v + default: + return nil, fmt.Errorf("unknown key %q in OCI config", k) + } + } + + ociConfigs = append(ociConfigs, ociConfig) + } + return &api.MetalConfig{ Debug: metalHammerDebug, GRPCAddress: grpcAddress, @@ -231,5 +271,6 @@ func getMetalAPIConfig(cmd *cobra.Command) (*api.MetalConfig, error) { NTPServers: ntpServers, Logging: logging, Partition: partition, + OciConfig: ociConfigs, }, nil } From 380592aaca05973725948beae2301e6b7227dc63 Mon Sep 17 00:00:00 2001 From: mac641 Date: Wed, 3 Dec 2025 15:39:45 +0100 Subject: [PATCH 4/6] fix(api): make OciConfig RegistryURL optional --- api/api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/api.go b/api/api.go index 201c1d9..3f63e67 100644 --- a/api/api.go +++ b/api/api.go @@ -48,7 +48,7 @@ type CertificateAuth struct { type OciConfig struct { // URL pointing to the oci registry - RegistryURL string `json:"registry_url"` + RegistryURL string `json:"registry_url,omitempty"` // Username that is capable of logging in to the registry Username string `json:"username,omitempty"` // Password for the user From 3445ea794d7c3e5150744523af6f22a86e18fb00 Mon Sep 17 00:00:00 2001 From: mac641 Date: Wed, 28 Jan 2026 15:35:14 +0100 Subject: [PATCH 5/6] refactor(api): introduce map of registryURL -> ociCredentials as suggested by @majst01 --- api/api.go | 8 +++----- pixiecore/cli/grpccmd.go | 17 ++++++++++------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/api/api.go b/api/api.go index 3f63e67..447868c 100644 --- a/api/api.go +++ b/api/api.go @@ -13,8 +13,8 @@ type MetalConfig struct { NTPServers []string `json:"ntp_servers,omitempty"` Partition string `json:"partition"` // Logging contains logging configurations passed to metal-hammer - Logging *Logging `json:"logging,omitempty"` - OciConfig []*OciConfig `json:"oci_config,omitempty"` + Logging *Logging `json:"logging,omitempty"` + OciConfigs map[string]*OciCredentials `json:"oci_config,omitempty"` } type Logging struct { @@ -46,9 +46,7 @@ type CertificateAuth struct { InsecureSkipVerify bool `json:"insecure_skip_verify,omitempty"` } -type OciConfig struct { - // URL pointing to the oci registry - RegistryURL string `json:"registry_url,omitempty"` +type OciCredentials struct { // Username that is capable of logging in to the registry Username string `json:"username,omitempty"` // Password for the user diff --git a/pixiecore/cli/grpccmd.go b/pixiecore/cli/grpccmd.go index a050f8f..f97905a 100644 --- a/pixiecore/cli/grpccmd.go +++ b/pixiecore/cli/grpccmd.go @@ -227,10 +227,13 @@ func getMetalAPIConfig(cmd *cobra.Command) (*api.MetalConfig, error) { return nil, fmt.Errorf("error reading flag: %w", err) } - var ociConfigs []*api.OciConfig + ociConfigs := make(map[string]*api.OciCredentials) for _, c := range metalHammerOciConfigs { - var ociConfig *api.OciConfig + var ( + ociCredentials *api.OciCredentials + registryURL string + ) parts := strings.SplitSeq(c, ",") for p := range parts { @@ -246,17 +249,17 @@ func getMetalAPIConfig(cmd *cobra.Command) (*api.MetalConfig, error) { if v == "" { return nil, fmt.Errorf("no registry url specified for oci config: %s", c) } - ociConfig.RegistryURL = v + registryURL = v case "username": - ociConfig.Username = v + ociCredentials.Username = v case "password": - ociConfig.Password = v + ociCredentials.Password = v default: return nil, fmt.Errorf("unknown key %q in OCI config", k) } } - ociConfigs = append(ociConfigs, ociConfig) + ociConfigs[registryURL] = ociCredentials } return &api.MetalConfig{ @@ -271,6 +274,6 @@ func getMetalAPIConfig(cmd *cobra.Command) (*api.MetalConfig, error) { NTPServers: ntpServers, Logging: logging, Partition: partition, - OciConfig: ociConfigs, + OciConfigs: ociConfigs, }, nil } From 27a64e8c02dec0296db0c5a7cb077b90b95f61f7 Mon Sep 17 00:00:00 2001 From: mac641 Date: Thu, 19 Feb 2026 15:18:10 +0100 Subject: [PATCH 6/6] refactor(grpccmd): make oci config use yaml file in order to specify configs - mainly to increase readability and overall code quality --- api/api.go | 4 ++-- go.mod | 1 + go.sum | 2 ++ pixiecore/cli/grpccmd.go | 51 ++++++++++++++++++---------------------- 4 files changed, 28 insertions(+), 30 deletions(-) diff --git a/api/api.go b/api/api.go index 447868c..38e4bfa 100644 --- a/api/api.go +++ b/api/api.go @@ -48,9 +48,9 @@ type CertificateAuth struct { type OciCredentials struct { // Username that is capable of logging in to the registry - Username string `json:"username,omitempty"` + Username string `json:"username,omitempty" yaml:"username,omitempty"` // Password for the user - Password string `json:"password,omitempty"` + Password string `json:"password,omitempty" yaml:"password,omitempty"` } // LogType defines which logging backend should be used diff --git a/go.mod b/go.mod index 5d6e029..2ee4f6e 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/metal-stack/pixie go 1.25 require ( + github.com/goccy/go-yaml v1.19.2 github.com/metal-stack/metal-api v0.42.4 github.com/metal-stack/v v1.0.3 github.com/pin/tftp/v3 v3.1.0 diff --git a/go.sum b/go.sum index d09cf22..ca58f15 100644 --- a/go.sum +++ b/go.sum @@ -15,6 +15,8 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= +github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= diff --git a/pixiecore/cli/grpccmd.go b/pixiecore/cli/grpccmd.go index f97905a..3806fc3 100644 --- a/pixiecore/cli/grpccmd.go +++ b/pixiecore/cli/grpccmd.go @@ -20,6 +20,7 @@ import ( "os" "strings" + yaml "github.com/goccy/go-yaml" "github.com/metal-stack/pixie/api" "github.com/metal-stack/pixie/pixiecore" "github.com/spf13/cobra" @@ -222,44 +223,38 @@ func getMetalAPIConfig(cmd *cobra.Command) (*api.MetalConfig, error) { } } - metalHammerOciConfigs, err := cmd.Flags().GetStringSlice("metal-hammer-oci-configs") + metalHammerOciConfigFilePath, err := cmd.Flags().GetString("metal-hammer-oci-config-file-path") if err != nil { return nil, fmt.Errorf("error reading flag: %w", err) } ociConfigs := make(map[string]*api.OciCredentials) + if metalHammerOciConfigFilePath != "" { + metalHammerOciConfigFileContent, err := os.ReadFile(metalHammerOciConfigFilePath) + if err != nil { + return nil, fmt.Errorf("error retrieving oci configs file: %w", err) + } - for _, c := range metalHammerOciConfigs { - var ( - ociCredentials *api.OciCredentials - registryURL string - ) + type MetalHammerOciConfig struct { + RegistryURL string `yaml:"registry-url"` + Credentials *api.OciCredentials + } + type MetalHammerOciConfigs struct { + OciConfigs []MetalHammerOciConfig `yaml:"oci-configs"` + } + var metalHammerOciConfigs MetalHammerOciConfigs + err = yaml.Unmarshal(metalHammerOciConfigFileContent, &metalHammerOciConfigs) + if err != nil { + return nil, fmt.Errorf("error parsing oci configs: %w", err) + } - parts := strings.SplitSeq(c, ",") - for p := range parts { - kv := strings.SplitN(strings.TrimSpace(p), "=", 2) - if len(kv) != 2 { - return nil, fmt.Errorf("invalid key-value pair in OCI config: %q", p) + for _, config := range metalHammerOciConfigs.OciConfigs { + if config.RegistryURL == "" { + return nil, fmt.Errorf("no registry url specified for oci config: %+v", config) } - k := strings.ToLower(strings.TrimSpace(kv[0])) - v := strings.TrimSpace(kv[1]) - switch k { - case "registry_url": - if v == "" { - return nil, fmt.Errorf("no registry url specified for oci config: %s", c) - } - registryURL = v - case "username": - ociCredentials.Username = v - case "password": - ociCredentials.Password = v - default: - return nil, fmt.Errorf("unknown key %q in OCI config", k) - } + ociConfigs[config.RegistryURL] = config.Credentials } - - ociConfigs[registryURL] = ociCredentials } return &api.MetalConfig{