55
66//go:build kubeapiserver
77
8- package autoinstrumentation
8+ package imageresolver
99
1010import (
11- "encoding/json"
1211 "fmt"
1312 "reflect"
1413 "strings"
1514 "sync"
1615 "time"
1716
18- "github.com/opencontainers/go-digest"
19-
2017 "github.com/DataDog/datadog-agent/pkg/clusteragent/admission/metrics"
21- "github.com/DataDog/datadog-agent/pkg/clusteragent/admission/mutate/autoinstrumentation/imageresolver"
2218 "github.com/DataDog/datadog-agent/pkg/remoteconfig/state"
2319 "github.com/DataDog/datadog-agent/pkg/util/log"
2420)
2521
26- // ImageResolver resolves container image references from tag-based to digest-based.
27- type ImageResolver interface {
22+ // Resolver resolves container image references from tag-based to digest-based.
23+ type Resolver interface {
2824 // Resolve takes a registry, repository, and tag string (e.g., "gcr.io/datadoghq", "dd-lib-python-init", "v3")
2925 // and returns a resolved image reference (e.g., "gcr.io/datadoghq/dd-lib-python-init@sha256:abc123")
3026 // If resolution fails or is not available, it returns the original image reference ("gcr.io/datadoghq/dd-lib-python-init:v3", false).
@@ -33,24 +29,25 @@ type ImageResolver interface {
3329
3430// noOpImageResolver is a simple implementation that returns the original image unchanged.
3531// This is used when no remote config client is available.
36- type noOpImageResolver struct {}
32+ type noOpResolver struct {}
3733
38- // newNoOpImageResolver creates a new noOpImageResolver.
39- func newNoOpImageResolver () ImageResolver {
40- return & noOpImageResolver {}
34+ // NewNoOpResolver creates a new noOpImageResolver.
35+ // This is useful for testing or when image resolution is not needed.
36+ func NewNoOpResolver () Resolver {
37+ return & noOpResolver {}
4138}
4239
4340// ResolveImage returns the original image reference.
44- func (r * noOpImageResolver ) Resolve (registry string , repository string , tag string ) (* ResolvedImage , bool ) {
41+ func (r * noOpResolver ) Resolve (registry string , repository string , tag string ) (* ResolvedImage , bool ) {
4542 log .Debugf ("Cannot resolve %s/%s:%s without remote config" , registry , repository , tag )
4643 metrics .ImageResolutionAttempts .Inc (repository , tag , tag )
4744 return nil , false
4845}
4946
5047// remoteConfigImageResolver resolves image references using remote configuration data.
5148// It maintains a cache of image mappings received from the remote config service.
52- type remoteConfigImageResolver struct {
53- rcClient imageresolver. RemoteConfigClient
49+ type rcResolver struct {
50+ rcClient RemoteConfigClient
5451
5552 mu sync.RWMutex
5653 imageMappings map [string ]map [string ]ImageInfo // repository name -> tag -> resolved image
@@ -61,8 +58,8 @@ type remoteConfigImageResolver struct {
6158 retryDelay time.Duration
6259}
6360
64- func newRcImageResolver (cfg imageresolver. Config ) ImageResolver {
65- resolver := & remoteConfigImageResolver {
61+ func newRcResolver (cfg Config ) Resolver {
62+ resolver := & rcResolver {
6663 rcClient : cfg .RCClient ,
6764 imageMappings : make (map [string ]map [string ]ImageInfo ),
6865 maxRetries : cfg .MaxInitRetries ,
@@ -84,7 +81,7 @@ func newRcImageResolver(cfg imageresolver.Config) ImageResolver {
8481 return resolver
8582}
8683
87- func (r * remoteConfigImageResolver ) waitForInitialConfig () error {
84+ func (r * rcResolver ) waitForInitialConfig () error {
8885 if currentConfigs := r .rcClient .GetConfigs (state .ProductGradualRollout ); len (currentConfigs ) > 0 {
8986 log .Debugf ("Initial configs available immediately: %d configurations" , len (currentConfigs ))
9087 r .updateCache (currentConfigs )
@@ -112,7 +109,7 @@ func (r *remoteConfigImageResolver) waitForInitialConfig() error {
112109// Output: "dd-lib-python-init@sha256:abc123...", true
113110// If resolution fails or is not available, it returns nil.
114111// Output: nil, false
115- func (r * remoteConfigImageResolver ) Resolve (registry string , repository string , tag string ) (* ResolvedImage , bool ) {
112+ func (r * rcResolver ) Resolve (registry string , repository string , tag string ) (* ResolvedImage , bool ) {
116113 if ! isDatadoghqRegistry (registry , r .datadoghqRegistries ) {
117114 log .Debugf ("Not a Datadoghq registry, not resolving" )
118115 return nil , false
@@ -148,20 +145,9 @@ func (r *remoteConfigImageResolver) Resolve(registry string, repository string,
148145 return resolvedImage , true
149146}
150147
151- func isDatadoghqRegistry (registry string , datadoghqRegistries map [string ]struct {}) bool {
152- _ , exists := datadoghqRegistries [registry ]
153- return exists
154- }
155-
156- // isValidDigest validates that a digest string follows the OCI image specification format
157- func isValidDigest (digestStr string ) bool {
158- _ , err := digest .Parse (digestStr )
159- return err == nil
160- }
161-
162148// updateCache processes configuration data and updates the image mappings cache.
163149// This is the core logic shared by both initialization and remote config updates.
164- func (r * remoteConfigImageResolver ) updateCache (configs map [string ]state.RawConfig ) {
150+ func (r * rcResolver ) updateCache (configs map [string ]state.RawConfig ) {
165151 validConfigs , errors := parseAndValidateConfigs (configs )
166152
167153 for configKey , err := range errors {
@@ -171,26 +157,7 @@ func (r *remoteConfigImageResolver) updateCache(configs map[string]state.RawConf
171157 r .updateCacheFromParsedConfigs (validConfigs )
172158}
173159
174- func parseAndValidateConfigs (configs map [string ]state.RawConfig ) (map [string ]RepositoryConfig , map [string ]error ) {
175- validConfigs := make (map [string ]RepositoryConfig )
176- errors := make (map [string ]error )
177- for configKey , rawConfig := range configs {
178- var repo RepositoryConfig
179- if err := json .Unmarshal (rawConfig .Config , & repo ); err != nil {
180- errors [configKey ] = fmt .Errorf ("failed to unmarshal: %w" , err )
181- continue
182- }
183-
184- if repo .RepositoryName == "" {
185- errors [configKey ] = fmt .Errorf ("missing repository_name in config %s" , configKey )
186- continue
187- }
188- validConfigs [configKey ] = repo
189- }
190- return validConfigs , errors
191- }
192-
193- func (r * remoteConfigImageResolver ) updateCacheFromParsedConfigs (validConfigs map [string ]RepositoryConfig ) {
160+ func (r * rcResolver ) updateCacheFromParsedConfigs (validConfigs map [string ]RepositoryConfig ) {
194161 newCache := make (map [string ]map [string ]ImageInfo )
195162
196163 for _ , repo := range validConfigs {
@@ -225,7 +192,7 @@ func (r *remoteConfigImageResolver) updateCacheFromParsedConfigs(validConfigs ma
225192// - When processUpdate is called, it receives the complete current state via GetConfigs()
226193// - If a repository is not in the update, it means it's no longer active
227194// - Therefore, replacing the entire cache ensures we stay in sync with remote config
228- func (r * remoteConfigImageResolver ) processUpdate (update map [string ]state.RawConfig , applyStateCallback func (string , state.ApplyStatus )) {
195+ func (r * rcResolver ) processUpdate (update map [string ]state.RawConfig , applyStateCallback func (string , state.ApplyStatus )) {
229196 validConfigs , errors := parseAndValidateConfigs (update )
230197
231198 r .updateCacheFromParsedConfigs (validConfigs )
@@ -244,48 +211,13 @@ func (r *remoteConfigImageResolver) processUpdate(update map[string]state.RawCon
244211 }
245212}
246213
247- // ImageInfo represents information about an image from remote configuration.
248- type ImageInfo struct {
249- Tag string `json:"tag"`
250- Digest string `json:"digest"`
251- CanonicalVersion string `json:"canonical_version"`
252- }
253-
254- // RepositoryConfig represents a repository configuration from remote config.
255- type RepositoryConfig struct {
256- RepositoryName string `json:"repository_name"`
257- Images []ImageInfo `json:"images"`
258- }
259-
260- // ResolvedImage represents a fully resolved image with digest and metadata.
261- type ResolvedImage struct {
262- FullImageRef string // e.g., "gcr.io/datadoghq/dd-lib-python-init@sha256:abc123..."
263- CanonicalVersion string // e.g., "3.1.0"
264- }
265-
266- func newResolvedImage (registry string , repositoryName string , imageInfo ImageInfo ) * ResolvedImage {
267- return & ResolvedImage {
268- FullImageRef : registry + "/" + repositoryName + "@" + imageInfo .Digest ,
269- CanonicalVersion : imageInfo .CanonicalVersion ,
270- }
271- }
272-
273- // NewImageResolver creates the appropriate ImageResolver based on whether
214+ // New creates the appropriate Resolver based on whether
274215// a remote config client is available.
275- func NewImageResolver (cfg imageresolver. Config ) ImageResolver {
216+ func New (cfg Config ) Resolver {
276217 if cfg .RCClient == nil || reflect .ValueOf (cfg .RCClient ).IsNil () {
277218 log .Debugf ("No remote config client available" )
278- return newNoOpImageResolver ()
219+ return NewNoOpResolver ()
279220 }
280221
281- return newRcImageResolver (cfg )
282- }
283-
284- // DEV: Delete this in favor of the imageresolver package one after the refactor is complete
285- func newDatadoghqRegistries (datadogRegistriesList []string ) map [string ]struct {} {
286- datadoghqRegistries := make (map [string ]struct {})
287- for _ , registry := range datadogRegistriesList {
288- datadoghqRegistries [registry ] = struct {}{}
289- }
290- return datadoghqRegistries
222+ return newRcResolver (cfg )
291223}
0 commit comments