Skip to content

Commit 2747eda

Browse files
committed
feat: Pass Package Registry proxy configuration to Hermeto
This change makes Hermeto executor aware of ConfigMap and populates Hermeto environment with key-value pairs from it. The main goal of that is to share registry proxy URLs with Hermeto. This change also adds necessary machinery to disable package registries proxies for Hermeto basing on local state of a pipeline and global ConfigMap configuration. Signed-off-by: Alexey Ovchinnikov <aovchinn@redhat.com>
1 parent 722544d commit 2747eda

File tree

6 files changed

+195
-14
lines changed

6 files changed

+195
-14
lines changed

pkg/cliwrappers/hermeto.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ package cliwrappers
44

55
import (
66
"errors"
7+
"os"
78

89
"github.com/konflux-ci/konflux-build-cli/pkg/logger"
910
)
@@ -19,9 +20,10 @@ type HermetoCliInterface interface {
1920

2021
type HermetoCli struct {
2122
Executor CliExecutorInterface
23+
Env []string // constructed as expected by exec.Cmd.Env
2224
}
2325

24-
func NewHermetoCli(executor CliExecutorInterface) (*HermetoCli, error) {
26+
func NewHermetoCli(executor CliExecutorInterface, env []string) (*HermetoCli, error) {
2527
hermetoCliAvailable, err := CheckCliToolAvailable("hermeto")
2628
if err != nil {
2729
return nil, err
@@ -31,7 +33,7 @@ func NewHermetoCli(executor CliExecutorInterface) (*HermetoCli, error) {
3133
return nil, errors.New("hermeto CLI is not available")
3234
}
3335

34-
return &HermetoCli{Executor: executor}, nil
36+
return &HermetoCli{Executor: executor, Env: env}, nil
3537
}
3638

3739
// Print the Hermeto version.
@@ -79,7 +81,8 @@ func (hc *HermetoCli) FetchDeps(params *HermetoFetchDepsParams) error {
7981
)
8082

8183
log.Debugf("Executing %s", shellJoin("hermeto", args...))
82-
_, _, _, err := hc.Executor.Execute(Cmd{Name: "hermeto", Args: args, LogOutput: true})
84+
extendedEnv := append(os.Environ(), hc.Env...)
85+
_, _, _, err := hc.Executor.Execute(Cmd{Name: "hermeto", Args: args, LogOutput: true, Env: extendedEnv})
8386
return err
8487
}
8588

pkg/cliwrappers/hermeto_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ import (
1010

1111
func setupHermetoCli() (*cliwrappers.HermetoCli, *mockExecutor) {
1212
executor := &mockExecutor{}
13-
hermetoCli := &cliwrappers.HermetoCli{Executor: executor}
13+
emptyEnv := []string{}
14+
hermetoCli := &cliwrappers.HermetoCli{Executor: executor, Env: emptyEnv}
1415
return hermetoCli, executor
1516
}
1617

pkg/commands/prefetch_dependencies/main.go

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"github.com/konflux-ci/konflux-build-cli/pkg/cliwrappers"
88
"github.com/konflux-ci/konflux-build-cli/pkg/common"
99
"github.com/konflux-ci/konflux-build-cli/pkg/logger"
10+
cfg "github.com/konflux-ci/konflux-build-cli/pkg/config"
1011

1112
"github.com/spf13/cobra"
1213
)
@@ -17,20 +18,59 @@ type PrefetchDependencies struct {
1718
Config *Params
1819
HermetoCli cliwrappers.HermetoCliInterface
1920
}
21+
type ConfigReaderFactory = func() (cfg.ConfigReader, error)
22+
23+
func getPackageProxyConfiguration(configReaderFactory ConfigReaderFactory) ([]string, error) {
24+
hermetoEnv := []string{}
25+
configMapReader, err := configReaderFactory()
26+
if err != nil { return hermetoEnv, err }
27+
configMap, err := configMapReader.ReadConfigData()
28+
if configMap != nil {
29+
if configMap.HermetoPackageRegistryProxyAllowed != "true" {
30+
log.Info("Not using package registry proxy because allow-package-registry-proxy " +
31+
"is not set to `true` on the cluster level")
32+
return hermetoEnv, err
33+
}
34+
// Note that empty URLs must be sanitized here, or it would result in validation
35+
// error in Hermeto.
36+
if configMap.HermetoNpmProxy != "" {
37+
envEntry := fmt.Sprintf("HERMETO_NPM__PROXY_URL=%s", configMap.HermetoNpmProxy)
38+
hermetoEnv = append(hermetoEnv, envEntry)
39+
}
40+
if configMap.HermetoYarnProxy != "" {
41+
envEntry := fmt.Sprintf("HERMETO_YARN__PROXY_URL=%s", configMap.HermetoYarnProxy)
42+
hermetoEnv = append(hermetoEnv, envEntry)
43+
}
44+
}
45+
46+
return hermetoEnv, err
47+
}
2048

2149
func New(cmd *cobra.Command) (*PrefetchDependencies, error) {
22-
config := Params{}
23-
if err := common.ParseParameters(cmd, ParamsConfig, &config); err != nil {
50+
var err error
51+
local_config := Params{}
52+
if err = common.ParseParameters(cmd, ParamsConfig, &local_config); err != nil {
2453
return nil, err
2554
}
2655

56+
hermetoEnv := []string{}
57+
if local_config.EnablePackageRegistryProxy {
58+
hermetoEnv, err = getPackageProxyConfiguration(cfg.NewConfigReader)
59+
if err != nil {
60+
log.Warnf("Failed to extract Hermeto environment settings from ConfigMap: %+v", err)
61+
}
62+
} else {
63+
log.Info("Not using package registry proxy because enable-package-registry-proxy is not set to `true` " +
64+
"on the pipeline level")
65+
}
66+
2767
executor := cliwrappers.NewCliExecutor()
28-
hermetoCli, err := cliwrappers.NewHermetoCli(executor)
68+
hermetoCli, err := cliwrappers.NewHermetoCli(executor, hermetoEnv)
2969
if err != nil {
3070
return nil, err
3171
}
3272

33-
prefetchDependencies := PrefetchDependencies{Config: &config, HermetoCli: hermetoCli}
73+
prefetchDependencies := PrefetchDependencies{Config: &local_config, HermetoCli: hermetoCli}
3474
return &prefetchDependencies, nil
3575
}
3676

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
package prefetch_dependencies
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"testing"
7+
8+
"github.com/konflux-ci/konflux-build-cli/pkg/config"
9+
. "github.com/onsi/gomega"
10+
11+
v1 "k8s.io/api/core/v1"
12+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
13+
fakeclient "k8s.io/client-go/kubernetes/fake"
14+
)
15+
16+
func Test_getHermetoEnvFromConfigMap(t *testing.T) {
17+
g := NewWithT(t)
18+
19+
testHttpProxy := "test.caching:3323"
20+
testNamespace := "test_namespace"
21+
testConfigMapName := "test_name"
22+
23+
t.Run("can successfully read a config map", func(t *testing.T) {
24+
fakeClient := fakeclient.NewClientset()
25+
fake_proxy_url := "https://www.example.com"
26+
fakeK8sConfigMapReader := &config.K8sConfigMapReader{Name: testConfigMapName, Namespace: testNamespace, Clientset: fakeClient}
27+
clusterConfigCMWithAllowCacheTrue := &v1.ConfigMap{
28+
ObjectMeta: metav1.ObjectMeta{
29+
Name: testConfigMapName,
30+
Namespace: testNamespace,
31+
},
32+
Data: map[string]string{
33+
"allow-cache-proxy": "true",
34+
"http-proxy": testHttpProxy,
35+
"no-proxy": "",
36+
"package-registry-proxy-npm-url": fake_proxy_url,
37+
"allow-package-registry-proxy": "true",
38+
},
39+
}
40+
ctx := context.Background()
41+
fakeClient.CoreV1().ConfigMaps(testNamespace).Create(ctx, clusterConfigCMWithAllowCacheTrue, metav1.CreateOptions{})
42+
fakeConfigReaderFactory := func() (config.ConfigReader, error) {
43+
return fakeK8sConfigMapReader, nil
44+
}
45+
expectedEnv := fmt.Sprintf("HERMETO_NPM__PROXY_URL=%s", fake_proxy_url)
46+
47+
parsed_config_map, err := getPackageProxyConfiguration(fakeConfigReaderFactory)
48+
49+
g.Expect(err).ToNot(HaveOccurred())
50+
g.Expect(parsed_config_map).ToNot(BeNil())
51+
g.Expect(parsed_config_map[0]).To(Equal(expectedEnv))
52+
})
53+
54+
t.Run("returns empty env when there is an error creating config map reader", func(t *testing.T) {
55+
fakeConfigReaderFactory := func() (config.ConfigReader, error) {
56+
return nil, fmt.Errorf("Fake error")
57+
}
58+
59+
parsed_config_map, err := getPackageProxyConfiguration(fakeConfigReaderFactory)
60+
61+
g.Expect(err).To(HaveOccurred())
62+
g.Expect(parsed_config_map).To(BeEmpty())
63+
})
64+
65+
t.Run("returns empty env when config map is empty", func(t *testing.T) {
66+
fakeClient := fakeclient.NewClientset()
67+
fakeK8sConfigMapReader := &config.K8sConfigMapReader{Name: testConfigMapName, Namespace: testNamespace, Clientset: fakeClient}
68+
clusterConfigCMWithAllowCacheTrue := &v1.ConfigMap{
69+
ObjectMeta: metav1.ObjectMeta{
70+
Name: testConfigMapName,
71+
Namespace: testNamespace,
72+
},
73+
Data: map[string]string{},
74+
}
75+
ctx := context.Background()
76+
fakeClient.CoreV1().ConfigMaps(testNamespace).Create(ctx, clusterConfigCMWithAllowCacheTrue, metav1.CreateOptions{})
77+
fakeConfigReaderFactory := func() (config.ConfigReader, error) {
78+
return fakeK8sConfigMapReader, nil
79+
}
80+
81+
parsed_config_map, err := getPackageProxyConfiguration(fakeConfigReaderFactory)
82+
83+
g.Expect(err).ToNot(HaveOccurred())
84+
g.Expect(parsed_config_map).To(BeEmpty())
85+
})
86+
87+
t.Run("returns empty env when no Hermeto fields are defined", func(t *testing.T) {
88+
fakeClient := fakeclient.NewClientset()
89+
fake_proxy_url := ""
90+
fakeK8sConfigMapReader := &config.K8sConfigMapReader{Name: testConfigMapName, Namespace: testNamespace, Clientset: fakeClient}
91+
clusterConfigCMWithAllowCacheTrue := &v1.ConfigMap{
92+
ObjectMeta: metav1.ObjectMeta{
93+
Name: testConfigMapName,
94+
Namespace: testNamespace,
95+
},
96+
Data: map[string]string{
97+
"allow-cache-proxy": "true",
98+
"http-proxy": testHttpProxy,
99+
"no-proxy": "",
100+
"hermeto-npm-proxy": fake_proxy_url,
101+
},
102+
}
103+
ctx := context.Background()
104+
fakeClient.CoreV1().ConfigMaps(testNamespace).Create(ctx, clusterConfigCMWithAllowCacheTrue, metav1.CreateOptions{})
105+
fakeConfigReaderFactory := func() (config.ConfigReader, error) {
106+
return fakeK8sConfigMapReader, nil
107+
}
108+
109+
parsed_config_map, err := getPackageProxyConfiguration(fakeConfigReaderFactory)
110+
111+
g.Expect(err).ToNot(HaveOccurred())
112+
g.Expect(parsed_config_map).ToNot(BeNil())
113+
g.Expect(parsed_config_map).To(BeEmpty())
114+
})
115+
}

pkg/commands/prefetch_dependencies/params.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,14 @@ var ParamsConfig = map[string]common.Parameter{
9595
Usage: "directory with git auth credentials (.git-credentials, .gitconfig or username/password)",
9696
Required: false,
9797
},
98+
"enable-package-registry-proxy": { // Pipeline-level registry proxy switch.
99+
Name: "enable-package-registry-proxy",
100+
EnvVarName: "KBC_PD_ENABLE_PACKAGE_REGISTRY_PROXY",
101+
TypeKind: reflect.Bool,
102+
Usage: "Pipeline-level enable package registry proxy. Defaults to true.",
103+
DefaultValue: "true", // A pipline will use a proxy unless explicitly told otherwise.
104+
Required: false,
105+
},
98106
}
99107

100108
type Params struct {
@@ -109,4 +117,5 @@ type Params struct {
109117
RHSMOrg string `paramName:"rhsm-org"`
110118
RHSMActivationKey string `paramName:"rhsm-activation-key"`
111119
GitAuthDirectory string `paramName:"git-auth-directory"`
120+
EnablePackageRegistryProxy bool `paramName:"enable-package-registry-proxy"`
112121
}

pkg/config/config_reader.go

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,16 @@ import (
1212
)
1313

1414
type KonfluxInfo struct {
15-
AllowCacheProxy string
16-
HttpProxy string
17-
NoProxy string
15+
AllowCacheProxy string
16+
HttpProxy string
17+
NoProxy string
18+
19+
// Address of a package registry proxy serving npm packages
20+
HermetoNpmProxy string
21+
HermetoYarnProxy string
22+
// Global setting allowing of forbidding usage of package registry rpoxies
23+
// on the cluster level.
24+
HermetoPackageRegistryProxyAllowed string
1825
}
1926

2027
// ConfigReader defines the interface for reading config data.
@@ -58,6 +65,9 @@ func (y *IniFileReader) ReadConfigData() (*KonfluxInfo, error) {
5865
AllowCacheProxy: cfg.Section("cache-proxy").Key("allow-cache-proxy").String(),
5966
HttpProxy: cfg.Section("cache-proxy").Key("http-proxy").String(),
6067
NoProxy: cfg.Section("cache-proxy").Key("no-proxy").String(),
68+
HermetoNpmProxy: cfg.Section("artifact-registry").Key("package-registry-proxy-npm-url").String(),
69+
HermetoYarnProxy: cfg.Section("artifact-registry").Key("package-registry-proxy-yarn-url").String(),
70+
HermetoPackageRegistryProxyAllowed: cfg.Section("artifact-registry").Key("allow-package-registry-proxy").String(),
6171
}
6272

6373
return newCacheProxy, nil
@@ -71,9 +81,12 @@ func (k *K8sConfigMapReader) ReadConfigData() (*KonfluxInfo, error) {
7181
return nil, fmt.Errorf("failed to get configmap %s/%s: %w", k.Namespace, k.Name, err)
7282
}
7383
newCacheProxy := &KonfluxInfo{
74-
AllowCacheProxy: configMap.Data["allow-cache-proxy"],
75-
HttpProxy: configMap.Data["http-proxy"],
76-
NoProxy: configMap.Data["no-proxy"],
84+
AllowCacheProxy: configMap.Data["allow-cache-proxy"],
85+
HttpProxy: configMap.Data["http-proxy"],
86+
NoProxy: configMap.Data["no-proxy"],
87+
HermetoNpmProxy: configMap.Data["package-registry-proxy-npm-url"],
88+
HermetoYarnProxy: configMap.Data["package-registry-proxy-yarn-url"],
89+
HermetoPackageRegistryProxyAllowed: configMap.Data["allow-package-registry-proxy"],
7790
}
7891
return newCacheProxy, nil
7992
}

0 commit comments

Comments
 (0)