| 94 | } |
| 95 | |
| 96 | func getPolicyThroughCache(ctx context.Context, s PolicySource, workDir string, dl func(string, string) (metadata.Metadata, error)) (string, metadata.Metadata, error) { |
| 97 | sourceUrl := s.PolicyUrl() |
| 98 | dest := uniqueDestination(workDir, s.Subdir(), sourceUrl) |
| 99 | |
| 100 | // Load or store the downloaded policy file from the given source URL. |
| 101 | // If the file is already in the download cache, it is loaded from there. |
| 102 | // Otherwise, it is downloaded from the source URL and stored in the cache. |
| 103 | dfn, _ := downloadCache.LoadOrStore(sourceUrl, sync.OnceValues(func() (string, cacheContent) { |
| 104 | log.Debugf("Download cache miss: %s", sourceUrl) |
| 105 | // Checkout policy repo into work directory. |
| 106 | log.Debugf("Downloading policy files from source url %s to destination %s", sourceUrl, dest) |
| 107 | m, err := dl(sourceUrl, dest) |
| 108 | c := &cacheContent{sourceUrl, m, err} |
| 109 | return dest, *c |
| 110 | })) |
| 111 | |
| 112 | d, c := dfn.(func() (string, cacheContent))() |
| 113 | if c.err != nil { |
| 114 | return "", c.metadata, c.err |
| 115 | } |
| 116 | |
| 117 | fs := utils.FS(ctx) |
| 118 | |
| 119 | // If the destination directory is different from the source directory, we |
| 120 | // need to symlink the source directory to the destination directory. |
| 121 | // Use synchronization to prevent race conditions when multiple goroutines |
| 122 | // try to create the same symlink simultaneously. |
| 123 | if filepath.Dir(dest) != filepath.Dir(d) { |
| 124 | // Get or create a mutex for this specific destination |
| 125 | mutexValue, _ := symlinkMutexes.LoadOrStore(dest, &sync.Mutex{}) |
| 126 | mutex := mutexValue.(*sync.Mutex) |
| 127 | |
| 128 | mutex.Lock() |
| 129 | defer mutex.Unlock() |
| 130 | |
| 131 | // Check again if the destination exists after acquiring the lock |
| 132 | if _, err := fs.Stat(dest); err == nil { |
| 133 | return dest, c.metadata, nil |
| 134 | } |
| 135 | |
| 136 | base := filepath.Dir(dest) |
| 137 | if err := fs.MkdirAll(base, 0o755); err != nil { |
| 138 | return "", nil, err |
| 139 | } |
| 140 | |
| 141 | if symlinkableFS, ok := fs.(afero.Symlinker); ok { |
| 142 | log.Debugf("Symlinking %s to %s", d, dest) |
| 143 | if err := symlinkableFS.SymlinkIfPossible(d, dest); err != nil { |
| 144 | return "", nil, err |
| 145 | } |
| 146 | logMetadata(c.metadata) |
| 147 | return dest, c.metadata, nil |
| 148 | } else { |
| 149 | log.Debugf("Filesystem does not support symlinking: %q, re-downloading instead", fs.Name()) |
| 150 | m, err := dl(sourceUrl, dest) |
| 151 | logMetadata(m) |
| 152 | return dest, m, err |
| 153 | } |