diff --git a/api/v1beta2/imagerepository_types.go b/api/v1beta2/imagerepository_types.go
index 15311bef..ace74337 100644
--- a/api/v1beta2/imagerepository_types.go
+++ b/api/v1beta2/imagerepository_types.go
@@ -57,6 +57,11 @@ type ImageRepositorySpec struct {
// +optional
SecretRef *meta.LocalObjectReference `json:"secretRef,omitempty"`
+ // ProxySecretRef specifies the Secret containing the proxy configuration
+ // to use while communicating with the container registry.
+ // +optional
+ ProxySecretRef *meta.LocalObjectReference `json:"proxySecretRef,omitempty"`
+
// ServiceAccountName is the name of the Kubernetes ServiceAccount used to authenticate
// the image pull if the service account has attached pull secrets.
// +kubebuilder:validation:MaxLength=253
diff --git a/api/v1beta2/zz_generated.deepcopy.go b/api/v1beta2/zz_generated.deepcopy.go
index c2928959..b486749c 100644
--- a/api/v1beta2/zz_generated.deepcopy.go
+++ b/api/v1beta2/zz_generated.deepcopy.go
@@ -248,6 +248,11 @@ func (in *ImageRepositorySpec) DeepCopyInto(out *ImageRepositorySpec) {
*out = new(meta.LocalObjectReference)
**out = **in
}
+ if in.ProxySecretRef != nil {
+ in, out := &in.ProxySecretRef, &out.ProxySecretRef
+ *out = new(meta.LocalObjectReference)
+ **out = **in
+ }
if in.CertSecretRef != nil {
in, out := &in.CertSecretRef, &out.CertSecretRef
*out = new(meta.LocalObjectReference)
diff --git a/config/crd/bases/image.toolkit.fluxcd.io_imagerepositories.yaml b/config/crd/bases/image.toolkit.fluxcd.io_imagerepositories.yaml
index dfa37b2c..e89a7403 100644
--- a/config/crd/bases/image.toolkit.fluxcd.io_imagerepositories.yaml
+++ b/config/crd/bases/image.toolkit.fluxcd.io_imagerepositories.yaml
@@ -373,6 +373,17 @@ spec:
- azure
- gcp
type: string
+ proxySecretRef:
+ description: |-
+ ProxySecretRef specifies the Secret containing the proxy configuration
+ to use while communicating with the container registry.
+ properties:
+ name:
+ description: Name of the referent.
+ type: string
+ required:
+ - name
+ type: object
secretRef:
description: |-
SecretRef can be given the name of a secret containing
diff --git a/docs/api/v1beta2/image-reflector.md b/docs/api/v1beta2/image-reflector.md
index eeac1e11..393523e2 100644
--- a/docs/api/v1beta2/image-reflector.md
+++ b/docs/api/v1beta2/image-reflector.md
@@ -451,6 +451,21 @@ equivalent.
+
serviceAccountName
string
diff --git a/docs/spec/v1beta2/imagerepositories.md b/docs/spec/v1beta2/imagerepositories.md
index 8ccb7f5d..372e79aa 100644
--- a/docs/spec/v1beta2/imagerepositories.md
+++ b/docs/spec/v1beta2/imagerepositories.md
@@ -229,6 +229,49 @@ data:
deprecated. If you have any Secrets using these keys and specified in an
ImageRepository, the controller will log a deprecation warning.
+### Proxy secret reference
+
+`.spec.proxySecretRef.name` is an optional field used to specify the name of a
+Secret that contains the proxy settings for the object. These settings are used
+for all the remote operations related to the ImageRepository.
+The Secret may contain three keys:
+
+- `address`, to specify the address of the proxy server. This is a required key.
+- `username`, to specify the username to use if the proxy server is protected by
+ basic authentication. This is an optional key.
+- `password`, to specify the password to use if the proxy server is protected by
+ basic authentication. This is an optional key.
+
+Example:
+
+```yaml
+apiVersion: image.toolkit.fluxcd.io/v1beta2
+kind: ImageRepository
+metadata:
+ name: example
+ namespace: default
+spec:
+ interval: 5m0s
+ url: example.com
+ proxySecretRef:
+ name: http-proxy
+---
+apiVersion: v1
+kind: Secret
+metadata:
+ name: http-proxy
+type: Opaque
+stringData:
+ address: http://proxy.com
+ username: mandalorian
+ password: grogu
+```
+
+Proxying can also be configured in the image-reflector-controller Deployment directly by
+using the standard environment variables such as `HTTPS_PROXY`, `ALL_PROXY`, etc.
+
+`.spec.proxySecretRef.name` takes precedence over all environment variables.
+
### Suspend
`.spec.suspend` is an optional field to suspend the reconciliation of an
diff --git a/go.mod b/go.mod
index 0bd62ca9..4595938d 100644
--- a/go.mod
+++ b/go.mod
@@ -8,11 +8,12 @@ require (
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6
github.com/Masterminds/semver/v3 v3.2.1
github.com/dgraph-io/badger/v3 v3.2103.5
+ github.com/elazarl/goproxy v0.0.0-20240726154733-8b0c20506380
github.com/fluxcd/image-reflector-controller/api v0.32.0
github.com/fluxcd/pkg/apis/acl v0.3.0
github.com/fluxcd/pkg/apis/event v0.10.0
github.com/fluxcd/pkg/apis/meta v1.6.0
- github.com/fluxcd/pkg/oci v0.40.0
+ github.com/fluxcd/pkg/oci v0.41.0
github.com/fluxcd/pkg/runtime v0.49.0
github.com/fluxcd/pkg/version v0.4.0
github.com/google/go-containerregistry v0.20.2
@@ -31,9 +32,9 @@ require (
require (
cloud.google.com/go/compute/metadata v0.3.0 // indirect
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect
- github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1 // indirect
- github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0 // indirect
- github.com/Azure/azure-sdk-for-go/sdk/internal v1.8.0 // indirect
+ github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0 // indirect
+ github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 // indirect
+ github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
github.com/Azure/go-autorest/autorest v0.11.29 // indirect
@@ -45,21 +46,21 @@ require (
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect
github.com/MakeNowJust/heredoc v1.0.0 // indirect
- github.com/aws/aws-sdk-go-v2 v1.30.3 // indirect
- github.com/aws/aws-sdk-go-v2/config v1.27.27 // indirect
- github.com/aws/aws-sdk-go-v2/credentials v1.17.27 // indirect
- github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 // indirect
- github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 // indirect
- github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 // indirect
- github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect
- github.com/aws/aws-sdk-go-v2/service/ecr v1.32.0 // indirect
+ github.com/aws/aws-sdk-go-v2 v1.30.4 // indirect
+ github.com/aws/aws-sdk-go-v2/config v1.27.29 // indirect
+ github.com/aws/aws-sdk-go-v2/credentials v1.17.29 // indirect
+ github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.12 // indirect
+ github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.16 // indirect
+ github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.16 // indirect
+ github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect
+ github.com/aws/aws-sdk-go-v2/service/ecr v1.32.2 // indirect
github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.21.5 // indirect
- github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 // indirect
- github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 // indirect
- github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 // indirect
- github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 // indirect
- github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 // indirect
- github.com/aws/smithy-go v1.20.3 // indirect
+ github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.4 // indirect
+ github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.18 // indirect
+ github.com/aws/aws-sdk-go-v2/service/sso v1.22.5 // indirect
+ github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.5 // indirect
+ github.com/aws/aws-sdk-go-v2/service/sts v1.30.5 // indirect
+ github.com/aws/smithy-go v1.20.4 // indirect
github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20230519004202-7f2db5bd753e // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
@@ -79,7 +80,7 @@ require (
github.com/evanphx/json-patch/v5 v5.9.0 // indirect
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect
github.com/fluxcd/cli-utils v0.36.0-flux.9 // indirect
- github.com/fluxcd/pkg/cache v0.0.2 // indirect
+ github.com/fluxcd/pkg/cache v0.0.3 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/go-errors/errors v1.5.1 // indirect
diff --git a/go.sum b/go.sum
index e8e3456b..fd8d0a0e 100644
--- a/go.sum
+++ b/go.sum
@@ -5,12 +5,12 @@ github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8af
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU=
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
-github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1 h1:E+OJmp2tPvt1W+amx48v1eqbjDYsgN+RzP4q16yV5eM=
-github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1/go.mod h1:a6xsAQUZg+VsS3TJ05SRp524Hs4pZ/AeFSr5ENf0Yjo=
-github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0 h1:U2rTu3Ef+7w9FHKIAXM6ZyqF3UOWJZ12zIm8zECAFfg=
-github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0/go.mod h1:9kIvujWAA58nmPmWB1m23fyWic1kYZMxD9CxaWn4Qpg=
-github.com/Azure/azure-sdk-for-go/sdk/internal v1.8.0 h1:jBQA3cKT4L2rWMpgE7Yt3Hwh2aUj8KXjIGLxjHeYNNo=
-github.com/Azure/azure-sdk-for-go/sdk/internal v1.8.0/go.mod h1:4OG6tQ9EOP/MT0NMjDlRzWoVFxfu9rN9B2X+tlSVktg=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0 h1:nyQWyZvwGTvunIMxi1Y9uXkcyr+I7TeNrr/foo4Kpk8=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0/go.mod h1:l38EPgmsp71HHLq9j7De57JcKOWPyhrsW1Awm1JS6K0=
+github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 h1:tfLQ34V6F7tVSwoTf/4lH5sE0o6eCJuNDTmH09nDpbc=
+github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0/go.mod h1:9kIvujWAA58nmPmWB1m23fyWic1kYZMxD9CxaWn4Qpg=
+github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY=
+github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
@@ -50,49 +50,49 @@ github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/aws/aws-sdk-go-v2 v1.18.0/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw=
-github.com/aws/aws-sdk-go-v2 v1.30.3 h1:jUeBtG0Ih+ZIFH0F4UkmL9w3cSpaMv9tYYDbzILP8dY=
-github.com/aws/aws-sdk-go-v2 v1.30.3/go.mod h1:nIQjQVp5sfpQcTc9mPSr1B0PaWK5ByX9MOoDadSN4lc=
+github.com/aws/aws-sdk-go-v2 v1.30.4 h1:frhcagrVNrzmT95RJImMHgabt99vkXGslubDaDagTk8=
+github.com/aws/aws-sdk-go-v2 v1.30.4/go.mod h1:CT+ZPWXbYrci8chcARI3OmI/qgd+f6WtuLOoaIA8PR0=
github.com/aws/aws-sdk-go-v2/config v1.18.25/go.mod h1:dZnYpD5wTW/dQF0rRNLVypB396zWCcPiBIvdvSWHEg4=
-github.com/aws/aws-sdk-go-v2/config v1.27.27 h1:HdqgGt1OAP0HkEDDShEl0oSYa9ZZBSOmKpdpsDMdO90=
-github.com/aws/aws-sdk-go-v2/config v1.27.27/go.mod h1:MVYamCg76dFNINkZFu4n4RjDixhVr51HLj4ErWzrVwg=
+github.com/aws/aws-sdk-go-v2/config v1.27.29 h1:+ZPKb3u9Up4KZWLGTtpTmC5T3XmRD1ZQ8XQjRCHUvJw=
+github.com/aws/aws-sdk-go-v2/config v1.27.29/go.mod h1:yxqvuubha9Vw8stEgNiStO+yZpP68Wm9hLmcm+R/Qk4=
github.com/aws/aws-sdk-go-v2/credentials v1.13.24/go.mod h1:jYPYi99wUOPIFi0rhiOvXeSEReVOzBqFNOX5bXYoG2o=
-github.com/aws/aws-sdk-go-v2/credentials v1.17.27 h1:2raNba6gr2IfA0eqqiP2XiQ0UVOpGPgDSi0I9iAP+UI=
-github.com/aws/aws-sdk-go-v2/credentials v1.17.27/go.mod h1:gniiwbGahQByxan6YjQUMcW4Aov6bLC3m+evgcoN4r4=
+github.com/aws/aws-sdk-go-v2/credentials v1.17.29 h1:CwGsupsXIlAFYuDVHv1nnK0wnxO0wZ/g1L8DSK/xiIw=
+github.com/aws/aws-sdk-go-v2/credentials v1.17.29/go.mod h1:BPJ/yXV92ZVq6G8uYvbU0gSl8q94UB63nMT5ctNO38g=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.3/go.mod h1:4Q0UFP0YJf0NrsEuEYHpM9fTSEVnD16Z3uyEF7J9JGM=
-github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 h1:KreluoV8FZDEtI6Co2xuNk/UqI9iwMrOx/87PBNIKqw=
-github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11/go.mod h1:SeSUYBLsMYFoRvHE0Tjvn7kbxaUhl75CJi1sbfhMxkU=
+github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.12 h1:yjwoSyDZF8Jth+mUk5lSPJCkMC0lMy6FaCD51jm6ayE=
+github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.12/go.mod h1:fuR57fAgMk7ot3WcNQfb6rSEn+SUffl7ri+aa8uKysI=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33/go.mod h1:7i0PF1ME/2eUPFcjkVIwq+DOygHEoK92t5cDqNgYbIw=
-github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 h1:SoNJ4RlFEQEbtDcCEt+QG56MY4fm4W8rYirAmq+/DdU=
-github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15/go.mod h1:U9ke74k1n2bf+RIgoX1SXFed1HLs51OgUSs+Ph0KJP8=
+github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.16 h1:TNyt/+X43KJ9IJJMjKfa3bNTiZbUP7DeCxfbTROESwY=
+github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.16/go.mod h1:2DwJF39FlNAUiX5pAc0UNeiz16lK2t7IaFcm0LFHEgc=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27/go.mod h1:UrHnn3QV/d0pBZ6QBAEQcqFLf8FAzLmoUfPVIueOvoM=
-github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 h1:C6WHdGnTDIYETAm5iErQUiVNsclNx9qbJVPIt03B6bI=
-github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15/go.mod h1:ZQLZqhcu+JhSrA9/NXRm8SkDvsycE+JkV3WGY41e+IM=
+github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.16 h1:jYfy8UPmd+6kJW5YhY0L1/KftReOGxI/4NtVSTh9O/I=
+github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.16/go.mod h1:7ZfEPZxkW42Afq4uQB8H2E2e6ebh6mXTueEpYzjCzcs=
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.34/go.mod h1:Etz2dj6UHYuw+Xw830KfzCfWGMzqvUTCjUj5b76GVDc=
-github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU=
-github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY=
+github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ=
+github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc=
github.com/aws/aws-sdk-go-v2/service/ecr v1.18.11/go.mod h1:Ce1q2jlNm8BVpjLaOnwnm5v2RClAbK6txwPljFzyW6c=
-github.com/aws/aws-sdk-go-v2/service/ecr v1.32.0 h1:lZoKOTEQUf5Oi9qVaZM/Hb0Z6SHIwwpDjbLFOVgB2t8=
-github.com/aws/aws-sdk-go-v2/service/ecr v1.32.0/go.mod h1:RhaP7Wil0+uuuhiE4FzOOEFZwkmFAk1ZflXzK+O3ptU=
+github.com/aws/aws-sdk-go-v2/service/ecr v1.32.2 h1:2RjzMZp/8PXJUMqiKkDSp7RVj6inF5DpVel35THjV+I=
+github.com/aws/aws-sdk-go-v2/service/ecr v1.32.2/go.mod h1:kdk+WJbHcGVbIlRQfSrKyuKkbWDdD8I9NScyS5vZ8eQ=
github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.16.2/go.mod h1:uHtRE7aqXNmpeYL+7Ec7LacH5zC9+w2T5MBOeEKDdu0=
github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.21.5 h1:PQp21GBlGNaQ+AVJAB8w2KTmLx0DkFS2fDET2Iy3+f0=
github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.21.5/go.mod h1:WMntdAol8KgeYsa5sDZPsRTXs4jVZIMYu0eQVVIQxnc=
-github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 h1:dT3MqvGhSoaIhRseqw2I0yH81l7wiR2vjs57O51EAm8=
-github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3/go.mod h1:GlAeCkHwugxdHaueRr4nhPuY+WW+gR8UjlcqzPr1SPI=
+github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.4 h1:KypMCbLPPHEmf9DgMGw51jMj77VfGPAN2Kv4cfhlfgI=
+github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.4/go.mod h1:Vz1JQXliGcQktFTN/LN6uGppAIRoLBR2bMvIMP0gOjc=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27/go.mod h1:EOwBD4J4S5qYszS5/3DpkejfuK+Z5/1uzICfPaZLtqw=
-github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 h1:HGErhhrxZlQ044RiM+WdoZxp0p+EGM62y3L6pwA4olE=
-github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17/go.mod h1:RkZEx4l0EHYDJpWppMJ3nD9wZJAa8/0lq9aVC+r2UII=
+github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.18 h1:tJ5RnkHCiSH0jyd6gROjlJtNwov0eGYNz8s8nFcR0jQ=
+github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.18/go.mod h1:++NHzT+nAF7ZPrHPsA+ENvsXkOO8wEu+C6RXltAG4/c=
github.com/aws/aws-sdk-go-v2/service/sso v1.12.10/go.mod h1:ouy2P4z6sJN70fR3ka3wD3Ro3KezSxU6eKGQI2+2fjI=
-github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 h1:BXx0ZIxvrJdSgSvKTZ+yRBeSqqgPM89VPlulEcl37tM=
-github.com/aws/aws-sdk-go-v2/service/sso v1.22.4/go.mod h1:ooyCOXjvJEsUw7x+ZDHeISPMhtwI3ZCB7ggFMcFfWLU=
+github.com/aws/aws-sdk-go-v2/service/sso v1.22.5 h1:zCsFCKvbj25i7p1u94imVoO447I/sFv8qq+lGJhRN0c=
+github.com/aws/aws-sdk-go-v2/service/sso v1.22.5/go.mod h1:ZeDX1SnKsVlejeuz41GiajjZpRSWR7/42q/EyA/QEiM=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.10/go.mod h1:AFvkxc8xfBe8XA+5St5XIHHrQQtkxqrRincx4hmMHOk=
-github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 h1:yiwVzJW2ZxZTurVbYWA7QOrAaCYQR72t0wrSBfoesUE=
-github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4/go.mod h1:0oxfLkpz3rQ/CHlx5hB7H69YUpFiI1tql6Q6Ne+1bCw=
+github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.5 h1:SKvPgvdvmiTWoi0GAJ7AsJfOz3ngVkD/ERbs5pUnHNI=
+github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.5/go.mod h1:20sz31hv/WsPa3HhU3hfrIet2kxM4Pe0r20eBZ20Tac=
github.com/aws/aws-sdk-go-v2/service/sts v1.19.0/go.mod h1:BgQOMsg8av8jset59jelyPW7NoZcZXLVpDsXunGDrk8=
-github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 h1:ZsDKRLXGWHk8WdtyYMoGNO7bTudrvuKpDKgMVRlepGE=
-github.com/aws/aws-sdk-go-v2/service/sts v1.30.3/go.mod h1:zwySh8fpFyXp9yOr/KVzxOl8SRqgf/IDw5aUt9UKFcQ=
+github.com/aws/aws-sdk-go-v2/service/sts v1.30.5 h1:OMsEmCyz2i89XwRwPouAJvhj81wINh+4UK+k/0Yo/q8=
+github.com/aws/aws-sdk-go-v2/service/sts v1.30.5/go.mod h1:vmSqFK+BVIwVpDAGZB3CoCXHzurt4qBE8lf+I/kRTh0=
github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
-github.com/aws/smithy-go v1.20.3 h1:ryHwveWzPV5BIof6fyDvor6V3iUL7nTfiTKXHiW05nE=
-github.com/aws/smithy-go v1.20.3/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E=
+github.com/aws/smithy-go v1.20.4 h1:2HK1zBdPgRbjFOHlfeQZfpC4r72MOb9bZkiFwggKO+4=
+github.com/aws/smithy-go v1.20.4/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20230519004202-7f2db5bd753e h1:hli0IOU73/tNWARHav2a41uMg7arHx0Qbhgcm4bDKXI=
github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20230519004202-7f2db5bd753e/go.mod h1:cheRroDS4qmOzi+Ue/oMHG4AV6n9F52W5QFdEKU59a0=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
@@ -144,6 +144,10 @@ github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRK
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
+github.com/elazarl/goproxy v0.0.0-20240726154733-8b0c20506380 h1:1NyRx2f4W4WBRyg0Kys0ZbaNmDDzZ2R/C7DTi+bbsJ0=
+github.com/elazarl/goproxy v0.0.0-20240726154733-8b0c20506380/go.mod h1:thX175TtLTzLj3p7N/Q9IiKZ7NF+p72cvL91emV0hzo=
+github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2 h1:dWB6v3RcOy03t/bUadywsbyrQwCqZeNIEX6M1OtSZOM=
+github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
github.com/emicklei/go-restful/v3 v3.12.0 h1:y2DdzBAURM29NFF94q6RaY4vjIH1rtwDapwQtU84iWk=
github.com/emicklei/go-restful/v3 v3.12.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
@@ -166,10 +170,10 @@ github.com/fluxcd/pkg/apis/event v0.10.0 h1:eMYXjMnLQ9jctPkTauuiBmEI127RjCKDf1zf
github.com/fluxcd/pkg/apis/event v0.10.0/go.mod h1:pG/3gbSBLNy6YGZP2eajiyVgkEQDvva789t46PY6NFE=
github.com/fluxcd/pkg/apis/meta v1.6.0 h1:93TcRpiph0OCoQh+cI+PM7E35kBW9dScuas9tWc90Dw=
github.com/fluxcd/pkg/apis/meta v1.6.0/go.mod h1:ZOeHcvyVdZDC5ZOGV7YuwplIvAx6LvmpeyhfTcNZCnc=
-github.com/fluxcd/pkg/cache v0.0.2 h1:+x1VCNDQbTQ5AbrOpMH3ps3NGek+qt52+6z7UjUP818=
-github.com/fluxcd/pkg/cache v0.0.2/go.mod h1:Xo09Wdo2YIiqyNrQbwvp83hIzxevznsvhcy+6xFjbcM=
-github.com/fluxcd/pkg/oci v0.40.0 h1:5T/Ya4f0hxx+Wl2X3EvUzunK74XMQsn4m/QS/8fFLXM=
-github.com/fluxcd/pkg/oci v0.40.0/go.mod h1:2/5L+XlMgac4dgqT/s5YnFzzOgAHqUJ6FlJmLhJEqms=
+github.com/fluxcd/pkg/cache v0.0.3 h1:VK5joG/p+amh5Ob+r1OFOx0cCYiswEf8mX1/J1BG7Mw=
+github.com/fluxcd/pkg/cache v0.0.3/go.mod h1:UU6oFhV+mG0A5/RwIlvXhyuKlJwQEkk92jVB3vKMLtk=
+github.com/fluxcd/pkg/oci v0.41.0 h1:oQh/VLv50q0+LTzbFfzjMGn7sDVykJo2dTb7GWJTHeU=
+github.com/fluxcd/pkg/oci v0.41.0/go.mod h1:iWUgmFelotr2aDbCyOTiGjqn6Vx86SYOv17L8sUi7/c=
github.com/fluxcd/pkg/runtime v0.49.0 h1:XldsD4C2TsfuIgku3NEQYCXFLZWDau22YqClTGUihVo=
github.com/fluxcd/pkg/runtime v0.49.0/go.mod h1:0JYsoNhrBtBC4mKAuZdfrkfIqsVGAXKM/A234HuNSnk=
github.com/fluxcd/pkg/version v0.4.0 h1:3F6oeIZ+ug/f7pALIBhcUhfURel37EPPOn7nsGfsnOg=
diff --git a/internal/controller/imagerepository_controller.go b/internal/controller/imagerepository_controller.go
index 1a557cf8..fcfce737 100644
--- a/internal/controller/imagerepository_controller.go
+++ b/internal/controller/imagerepository_controller.go
@@ -20,6 +20,8 @@ import (
"context"
"errors"
"fmt"
+ "net/http"
+ "net/url"
"regexp"
"sort"
"strings"
@@ -330,6 +332,23 @@ func (r *ImageRepositoryReconciler) setAuthOptions(ctx context.Context, obj *ima
ctx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()
+ var transportOptions []func(*http.Transport)
+
+ // Load proxy configuration.
+ var proxyURL *url.URL
+ var err error
+ if obj.Spec.ProxySecretRef != nil {
+ proxyURL, err = r.getProxyURL(ctx, obj)
+ if err != nil {
+ return nil, err
+ }
+ if proxyURL != nil {
+ transportOptions = append(transportOptions, func(t *http.Transport) {
+ t.Proxy = http.ProxyURL(proxyURL)
+ })
+ }
+ }
+
// Configure authentication strategy to access the registry.
var options []remote.Option
var authSecret corev1.Secret
@@ -357,7 +376,12 @@ func (r *ImageRepositoryReconciler) setAuthOptions(ctx context.Context, obj *ima
default:
opts = r.DeprecatedLoginOpts
}
- auth, authErr = login.NewManager().Login(ctx, obj.Spec.Image, ref, opts)
+ var managerOpts []login.Option
+ if proxyURL != nil {
+ managerOpts = append(managerOpts, login.WithProxyURL(proxyURL))
+ }
+ manager := login.NewManager(managerOpts...)
+ auth, authErr = manager.Login(ctx, obj.Spec.Image, ref, opts)
}
if authErr != nil {
// If it's not unconfigured provider error, abort reconciliation.
@@ -385,20 +409,33 @@ func (r *ImageRepositoryReconciler) setAuthOptions(ctx context.Context, obj *ima
}
}
- tr, err := secret.TransportFromKubeTLSSecret(&certSecret)
+ tlsConfig, err := secret.TLSConfigFromKubeTLSSecret(&certSecret)
if err != nil {
return nil, err
}
- if tr.TLSClientConfig == nil {
- tr, err = secret.TransportFromSecret(&certSecret)
+ if tlsConfig == nil {
+ tlsConfig, err = secret.TLSConfigFromSecret(&certSecret)
if err != nil {
return nil, err
}
- if tr.TLSClientConfig != nil {
+ if tlsConfig != nil {
ctrl.LoggerFrom(ctx).
Info("warning: specifying TLS auth data via `certFile`/`keyFile`/`caFile` is deprecated, please use `tls.crt`/`tls.key`/`ca.crt` instead")
}
}
+ if tlsConfig != nil {
+ transportOptions = append(transportOptions, func(t *http.Transport) {
+ t.TLSClientConfig = tlsConfig
+ })
+ }
+ }
+
+ // Specify any transport options.
+ if len(transportOptions) > 0 {
+ tr := http.DefaultTransport.(*http.Transport).Clone()
+ for _, opt := range transportOptions {
+ opt(tr)
+ }
options = append(options, remote.WithTransport(tr))
}
@@ -435,6 +472,40 @@ func (r *ImageRepositoryReconciler) setAuthOptions(ctx context.Context, obj *ima
return options, nil
}
+// getProxyURL gets the proxy configuration for the transport based on the
+// specified proxy secret reference in the ImageRepository object.
+func (r *ImageRepositoryReconciler) getProxyURL(ctx context.Context, obj *imagev1.ImageRepository) (*url.URL, error) {
+ if obj.Spec.ProxySecretRef == nil || obj.Spec.ProxySecretRef.Name == "" {
+ return nil, nil
+ }
+
+ proxySecretName := types.NamespacedName{
+ Namespace: obj.Namespace,
+ Name: obj.Spec.ProxySecretRef.Name,
+ }
+ var proxySecret corev1.Secret
+ if err := r.Get(ctx, proxySecretName, &proxySecret); err != nil {
+ return nil, err
+ }
+
+ proxyData := proxySecret.Data
+ address, ok := proxyData["address"]
+ if !ok {
+ return nil, fmt.Errorf("invalid proxy secret '%s/%s': key 'address' is missing",
+ obj.Namespace, obj.Spec.ProxySecretRef.Name)
+ }
+ proxyURL, err := url.Parse(string(address))
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse proxy address '%s': %w", address, err)
+ }
+ user, hasUser := proxyData["username"]
+ password, hasPassword := proxyData["password"]
+ if hasUser || hasPassword {
+ proxyURL.User = url.UserPassword(string(user), string(password))
+ }
+ return proxyURL, nil
+}
+
// shouldScan takes an image repo and the time now, and returns whether
// the repository should be scanned now, and how long to wait for the
// next scan. It also returns the reason for the scan.
diff --git a/internal/controller/imagerepository_controller_test.go b/internal/controller/imagerepository_controller_test.go
index a1fc6dcd..331bddba 100644
--- a/internal/controller/imagerepository_controller_test.go
+++ b/internal/controller/imagerepository_controller_test.go
@@ -145,6 +145,17 @@ func TestImageRepositoryReconciler_setAuthOptions(t *testing.T) {
corev1.TLSPrivateKeyKey: clientKeyPEM,
}
+ testProxySecret := &corev1.Secret{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "test-proxy-secret",
+ Namespace: testNamespace,
+ },
+ Type: corev1.SecretTypeOpaque,
+ Data: map[string][]byte{
+ "address": []byte("http://proxy.example.com"),
+ },
+ }
+
testDeprecatedTLSSecret := &corev1.Secret{}
testDeprecatedTLSSecret.Name = testDeprecatedTLSSecretName
testDeprecatedTLSSecret.Namespace = testNamespace
@@ -249,8 +260,28 @@ func TestImageRepositoryReconciler_setAuthOptions(t *testing.T) {
wantErr: true,
},
{
- name: "secret ref and cert secret ref",
- mockObjs: []client.Object{testSecret, testTLSSecret},
+ name: "proxy secret ref with existing secret",
+ mockObjs: []client.Object{testProxySecret},
+ imageRepoSpec: imagev1.ImageRepositorySpec{
+ Image: testImg,
+ ProxySecretRef: &meta.LocalObjectReference{
+ Name: testProxySecret.Name,
+ },
+ },
+ },
+ {
+ name: "proxy secret ref with non-existing secret",
+ imageRepoSpec: imagev1.ImageRepositorySpec{
+ Image: testImg,
+ ProxySecretRef: &meta.LocalObjectReference{
+ Name: "non-existing-secret",
+ },
+ },
+ wantErr: true,
+ },
+ {
+ name: "secret, cert secret and proxy secret refs",
+ mockObjs: []client.Object{testSecret, testTLSSecret, testProxySecret},
imageRepoSpec: imagev1.ImageRepositorySpec{
Image: testImg,
SecretRef: &meta.LocalObjectReference{
@@ -259,6 +290,9 @@ func TestImageRepositoryReconciler_setAuthOptions(t *testing.T) {
CertSecretRef: &meta.LocalObjectReference{
Name: testTLSSecretName,
},
+ ProxySecretRef: &meta.LocalObjectReference{
+ Name: testProxySecret.Name,
+ },
},
},
{
@@ -498,19 +532,22 @@ func TestImageRepositoryReconciler_scan(t *testing.T) {
registryServer := test.NewRegistryServer()
defer registryServer.Close()
+ proxyAddr, proxyPort := test.NewProxy(t)
+
tests := []struct {
name string
tags []string
exclusionList []string
annotation string
db *mockDatabase
- wantErr bool
+ proxyURL *url.URL
+ wantErr string
wantTags []string
wantLatestTags []string
}{
{
name: "no tags",
- wantErr: true,
+ wantErr: "404 Not Found",
},
{
name: "simple tags",
@@ -519,6 +556,23 @@ func TestImageRepositoryReconciler_scan(t *testing.T) {
wantTags: []string{"a", "b", "c", "d"},
wantLatestTags: []string{"d", "c", "b", "a"},
},
+ {
+ name: "simple tags with proxy",
+ tags: []string{"a", "b", "c", "d"},
+ db: &mockDatabase{},
+ proxyURL: &url.URL{Scheme: "http", Host: proxyAddr},
+ wantTags: []string{"a", "b", "c", "d"},
+ wantLatestTags: []string{"d", "c", "b", "a"},
+ },
+ {
+ name: "simple tags with incorrect proxy",
+ tags: []string{"a", "b", "c", "d"},
+ db: &mockDatabase{},
+ proxyURL: &url.URL{Scheme: "http", Host: fmt.Sprintf("localhost:%d", proxyPort+1)},
+ wantErr: "connection refused",
+ wantTags: []string{"a", "b", "c", "d"},
+ wantLatestTags: []string{"d", "c", "b", "a"},
+ },
{
name: "simple tags, 10+",
tags: []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k"},
@@ -546,13 +600,13 @@ func TestImageRepositoryReconciler_scan(t *testing.T) {
name: "bad exclusion pattern",
tags: []string{"a"}, // Ensure repo isn't empty to prevent 404.
exclusionList: []string{"[="},
- wantErr: true,
+ wantErr: "failed to compile regex",
},
{
name: "db write fails",
tags: []string{"a", "b"},
db: &mockDatabase{WriteError: errors.New("fail")},
- wantErr: true,
+ wantErr: "failed to set tags",
},
{
name: "with reconcile annotation",
@@ -592,8 +646,18 @@ func TestImageRepositoryReconciler_scan(t *testing.T) {
opts := []remote.Option{}
+ if tt.proxyURL != nil {
+ tr := &http.Transport{Proxy: http.ProxyURL(tt.proxyURL)}
+ opts = append(opts, remote.WithTransport(tr))
+ }
+
tagCount, err := r.scan(context.TODO(), repo, ref, opts)
- g.Expect(err != nil).To(Equal(tt.wantErr))
+ if tt.wantErr != "" {
+ g.Expect(err).To(HaveOccurred())
+ g.Expect(err.Error()).To(ContainSubstring(tt.wantErr))
+ } else {
+ g.Expect(err).NotTo(HaveOccurred())
+ }
if err == nil {
g.Expect(tagCount).To(Equal(len(tt.wantTags)))
g.Expect(r.Database.Tags(imgRepo)).To(Equal(tt.wantTags))
@@ -607,6 +671,191 @@ func TestImageRepositoryReconciler_scan(t *testing.T) {
}
}
+func TestImageRepositoryReconciler_getProxyURL(t *testing.T) {
+ tests := []struct {
+ name string
+ repo *imagev1.ImageRepository
+ objects []client.Object
+ wantURL string
+ wantErr string
+ }{
+ {
+ name: "empty proxySecretRef",
+ repo: &imagev1.ImageRepository{
+ Spec: imagev1.ImageRepositorySpec{
+ ProxySecretRef: nil,
+ },
+ },
+ },
+ {
+ name: "non-existing proxySecretRef",
+ repo: &imagev1.ImageRepository{
+ Spec: imagev1.ImageRepositorySpec{
+ ProxySecretRef: &meta.LocalObjectReference{
+ Name: "non-existing",
+ },
+ },
+ },
+ wantErr: "secrets \"non-existing\" not found",
+ },
+ {
+ name: "missing address in proxySecretRef",
+ repo: &imagev1.ImageRepository{
+ Spec: imagev1.ImageRepositorySpec{
+ ProxySecretRef: &meta.LocalObjectReference{
+ Name: "dummy",
+ },
+ },
+ },
+ objects: []client.Object{
+ &corev1.Secret{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "dummy",
+ },
+ Data: map[string][]byte{},
+ },
+ },
+ wantErr: "invalid proxy secret '/dummy': key 'address' is missing",
+ },
+ {
+ name: "invalid address in proxySecretRef",
+ repo: &imagev1.ImageRepository{
+ Spec: imagev1.ImageRepositorySpec{
+ ProxySecretRef: &meta.LocalObjectReference{
+ Name: "dummy",
+ },
+ },
+ },
+ objects: []client.Object{
+ &corev1.Secret{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "dummy",
+ },
+ Data: map[string][]byte{
+ "address": {0x7f},
+ },
+ },
+ },
+ wantErr: "failed to parse proxy address '\x7f': parse \"\\x7f\": net/url: invalid control character in URL",
+ },
+ {
+ name: "no user, no password",
+ repo: &imagev1.ImageRepository{
+ Spec: imagev1.ImageRepositorySpec{
+ ProxySecretRef: &meta.LocalObjectReference{
+ Name: "dummy",
+ },
+ },
+ },
+ objects: []client.Object{
+ &corev1.Secret{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "dummy",
+ },
+ Data: map[string][]byte{
+ "address": []byte("http://proxy.example.com"),
+ },
+ },
+ },
+ wantURL: "http://proxy.example.com",
+ },
+ {
+ name: "user, no password",
+ repo: &imagev1.ImageRepository{
+ Spec: imagev1.ImageRepositorySpec{
+ ProxySecretRef: &meta.LocalObjectReference{
+ Name: "dummy",
+ },
+ },
+ },
+ objects: []client.Object{
+ &corev1.Secret{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "dummy",
+ },
+ Data: map[string][]byte{
+ "address": []byte("http://proxy.example.com"),
+ "username": []byte("user"),
+ },
+ },
+ },
+ wantURL: "http://user:@proxy.example.com",
+ },
+ {
+ name: "no user, password",
+ repo: &imagev1.ImageRepository{
+ Spec: imagev1.ImageRepositorySpec{
+ ProxySecretRef: &meta.LocalObjectReference{
+ Name: "dummy",
+ },
+ },
+ },
+ objects: []client.Object{
+ &corev1.Secret{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "dummy",
+ },
+ Data: map[string][]byte{
+ "address": []byte("http://proxy.example.com"),
+ "password": []byte("password"),
+ },
+ },
+ },
+ wantURL: "http://:password@proxy.example.com",
+ },
+ {
+ name: "user, password",
+ repo: &imagev1.ImageRepository{
+ Spec: imagev1.ImageRepositorySpec{
+ ProxySecretRef: &meta.LocalObjectReference{
+ Name: "dummy",
+ },
+ },
+ },
+ objects: []client.Object{
+ &corev1.Secret{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "dummy",
+ },
+ Data: map[string][]byte{
+ "address": []byte("http://proxy.example.com"),
+ "username": []byte("user"),
+ "password": []byte("password"),
+ },
+ },
+ },
+ wantURL: "http://user:password@proxy.example.com",
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ g := NewWithT(t)
+
+ c := fake.NewClientBuilder().
+ WithScheme(testEnv.Scheme()).
+ WithObjects(tt.objects...).
+ Build()
+
+ r := &ImageRepositoryReconciler{
+ Client: c,
+ }
+
+ u, err := r.getProxyURL(ctx, tt.repo)
+ if tt.wantErr == "" {
+ g.Expect(err).To(BeNil())
+ } else {
+ g.Expect(err.Error()).To(ContainSubstring(tt.wantErr))
+ }
+ if tt.wantURL == "" {
+ g.Expect(u).To(BeNil())
+ } else {
+ g.Expect(u.String()).To(Equal(tt.wantURL))
+ }
+ })
+ }
+}
+
func TestGetLatestTags(t *testing.T) {
tests := []struct {
name string
diff --git a/internal/secret/secret.go b/internal/secret/secret.go
index 41e372a1..a226a93a 100644
--- a/internal/secret/secret.go
+++ b/internal/secret/secret.go
@@ -23,7 +23,6 @@ import (
"encoding/json"
"errors"
"fmt"
- "net/http"
"net/url"
"strings"
"testing"
@@ -45,54 +44,30 @@ type dockerConfig struct {
Auths map[string]authn.AuthConfig
}
-// TransportFromSecret reads the TLS data specified in the provided Secret
-// and returns a transport configured with the appropriate TLS settings.
+// TLSConfigFromSecret reads the TLS data specified in the provided Secret
+// and returns a tls.Config with the appropriate TLS settings.
// It checks for the following keys in the Secret:
// - `caFile`, for the CA certificate
// - `certFile` and `keyFile`, for the certificate and private key
//
-// If none of these keys exists in the Secret then an empty transport is
+// If none of these keys exists in the Secret then nil is
// returned. If only a certificate OR private key is found, an error is
// returned.
-func TransportFromSecret(certSecret *corev1.Secret) (*http.Transport, error) {
- // It's possible the secret doesn't contain any certs after
- // all and the default transport could be used; but it's
- // simpler here to assume a fresh transport is needed.
- transport := &http.Transport{}
- config, err := tlsConfigFromSecret(certSecret, false)
- if err != nil {
- return nil, err
- }
- if config != nil {
- transport.TLSClientConfig = config
- }
-
- return transport, nil
+func TLSConfigFromSecret(certSecret *corev1.Secret) (*tls.Config, error) {
+ return tlsConfigFromSecret(certSecret, false)
}
-// TransportFromKubeTLSSecret reads the TLS data specified in the provided
-// Secret and returns a transport configured with the appropriate TLS settings.
+// TLSConfigFromKubeTLSSecret reads the TLS data specified in the provided
+// Secret and returns a tls.Config with the appropriate TLS settings.
// It checks for the following keys in the Secret:
// - `ca.crt`, for the CA certificate
// - `tls.crt` and `tls.key`, for the certificate and private key
//
-// If none of these keys exists in the Secret then an empty transport is
+// If none of these keys exists in the Secret then nil is
// returned. If only a certificate OR private key is found, an error is
// returned.
-func TransportFromKubeTLSSecret(certSecret *corev1.Secret) (*http.Transport, error) {
- // It's possible the secret doesn't contain any certs after
- // all and the default transport could be used; but it's
- // simpler here to assume a fresh transport is needed.
- transport := &http.Transport{}
- config, err := tlsConfigFromSecret(certSecret, true)
- if err != nil {
- return nil, err
- }
- if config != nil {
- transport.TLSClientConfig = config
- }
-
- return transport, nil
+func TLSConfigFromKubeTLSSecret(certSecret *corev1.Secret) (*tls.Config, error) {
+ return tlsConfigFromSecret(certSecret, true)
}
// tlsClientConfigFromSecret attempts to construct and return a TLS client
diff --git a/internal/test/listener.go b/internal/test/listener.go
new file mode 100644
index 00000000..2fdc9389
--- /dev/null
+++ b/internal/test/listener.go
@@ -0,0 +1,48 @@
+/*
+Copyright 2024 The Flux authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package test
+
+import (
+ "net"
+ "strconv"
+ "strings"
+ "testing"
+)
+
+// NewListener creates a TCP listener on a random port and returns
+// the listener, the address and the port of this listener.
+// It also registers a cleanup function to close the listener
+// when the test ends.
+func NewListener(t *testing.T) (net.Listener, string, int) {
+ t.Helper()
+
+ lis, err := net.Listen("tcp", "localhost:0")
+ if err != nil {
+ t.Fatalf("failed to create listener: %v", err)
+ }
+ t.Cleanup(func() { lis.Close() })
+
+ addr := lis.Addr().String()
+ addrParts := strings.Split(addr, ":")
+ portStr := addrParts[len(addrParts)-1]
+ port, err := strconv.Atoi(portStr)
+ if err != nil {
+ t.Fatalf("failed to parse port: %v", err)
+ }
+
+ return lis, addr, port
+}
diff --git a/internal/test/proxy.go b/internal/test/proxy.go
new file mode 100644
index 00000000..11112ff3
--- /dev/null
+++ b/internal/test/proxy.go
@@ -0,0 +1,46 @@
+/*
+Copyright 2024 The Flux authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package test
+
+import (
+ "net/http"
+ "testing"
+
+ "github.com/elazarl/goproxy"
+)
+
+// NewProxy creates a new goproxy server on a random port and returns
+// the address and the port of this server. It also registers a
+// cleanup functions to close the server and the listener when
+// the test ends.
+func NewProxy(t *testing.T) (string, int) {
+ t.Helper()
+
+ lis, addr, port := NewListener(t)
+
+ handler := goproxy.NewProxyHttpServer()
+ handler.Verbose = true
+
+ server := &http.Server{
+ Addr: addr,
+ Handler: handler,
+ }
+ go server.Serve(lis)
+ t.Cleanup(func() { server.Close() })
+
+ return addr, port
+}
|