Skip to content

Commit 92e4d21

Browse files
committed
Improved version taking into account the last PR comments
1 parent 61b2580 commit 92e4d21

File tree

9 files changed

+191
-81
lines changed

9 files changed

+191
-81
lines changed

charts-syncer.yaml

+1-8
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,6 @@
44

55
# source includes relevant information about the source chart repository
66
source:
7-
# Dependencies located in repos from this list will be considered as trusted, and also synced.
8-
# The entry format is the same as "repo" (see below)
9-
trustedSourceDeps:
10-
- kind: HELM
11-
url: https://grafana.github.io/helm-charts
127
repo:
138
# Kind specify the chart repository kind. Valid values are HELM, CHARTMUSEUM, and HARBOR
149
kind: HELM
@@ -27,9 +22,7 @@ source:
2722
# chartsIndex: my-oci-registry.io/my-project/my-custom-index:prod
2823
# target includes relevant information about the target chart repository
2924
target:
30-
# In case there is a need to mirror dependencies (from trustedSourceDeps list, see above) - this must be set to true
31-
replaceDependencyRepo: true
32-
# repoName is used to modify the README of the chart. Default value: `myrepo`
25+
# repoName is used to modify the README of the chart. Default value: `myrepo`
3326
repoName: myrepo
3427
# containerRegistry is used to update the image registry section of the values.yaml file
3528
# NOTE: If containerRegistry is not set (or not present), the registry sections won't be updated

examples/sync-deps.yaml

+18-16
Original file line numberDiff line numberDiff line change
@@ -4,33 +4,35 @@
44

55
# source includes relevant information about the source chart repository
66
source:
7-
# Dependencies located in repos from this list will be considered as trusted, and also synced.
8-
# The entry format is the same as "repo" (see below)
9-
trustedSourceDeps:
10-
- kind: HELM
11-
url: https://grafana.github.io/helm-charts
7+
# Optional: Dependencies located in repos from this list will be considered as trusted and maintained as is
8+
# if there is a need to work with external repositories different from the source - they must be included here
9+
ignoreTrustedRepos:
10+
- kind: HELM
11+
url: https://grafana.github.io/helm-charts
1212
repo:
1313
# Kind specify the chart repository kind. Valid values are HELM, CHARTMUSEUM, and HARBOR
1414
kind: HELM
1515
# url is the url of the chart repository
16-
url: https://prometheus-community.github.io/helm-charts # local test source repo
16+
url: https://prometheus-community.github.io/helm-charts # local test source repo
17+
1718
# target includes relevant information about the target chart repository
1819
target:
19-
# In case there is a need to mirror dependencies (from trustedSourceDeps list, see above) - this must be set to true
20-
replaceDependencyRepo: true
20+
# repoName is used to modify the README of the chart. Default value: `myrepo`
21+
repoName: myrepo
22+
23+
# Optional: Dependencies located in repos from this list will be considered as trusted and also synced to the target.
24+
# This setting takes precedence of source.ignoreTrustedRepos for the same entry.
25+
# If there is a need to work with external repositories different from the source - they must be included here
26+
syncTrustedRepos:
27+
- kind: HELM
28+
url: https://grafana.github.io/helm-charts
2129
repo:
2230
# Kind specify the chart repository kind. Valid values are HELM, CHARTMUSEUM, and HARBOR
2331
kind: LOCAL
24-
path: localrepo
25-
# charts is an OPTIONAL list to specify a subset of charts to be synchronized
26-
# It is mandatory if the source repo is OCI and not autodiscovery is supported in that repository
27-
# More info here https://github.com/bitnami-labs/charts-syncer#charts-index-for-oci-based-repositories
32+
# url is the url of the chart repository
33+
path: localrepo # local test target repo
2834
charts:
2935
- kube-prometheus-stack
30-
# opt-out counterpart of "charts" property that explicit list the Helm charts to be skipped
31-
# either "charts" or "skipCharts" can be used at once
32-
# skipCharts:
33-
# - mariadb
3436

3537
# Whether to also relocate the container images referenced by the Helm Chart
3638
# Note that this requires the Helm Chart to be compatible with relok8s tool by containing a .relok8s-images.yaml file

internal/chart/dependency.go

+60-21
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,15 @@ import (
44
"bytes"
55
"encoding/json"
66
"fmt"
7-
"io/ioutil"
8-
"net/url"
9-
"os"
10-
"path"
11-
127
"github.com/juju/errors"
138
"github.com/mkmik/multierror"
149
"helm.sh/helm/v3/pkg/chart"
1510
"helm.sh/helm/v3/pkg/provenance"
11+
"io/ioutil"
1612
"k8s.io/klog"
13+
"net/url"
14+
"os"
15+
"path"
1716
"sigs.k8s.io/yaml"
1817

1918
"github.com/bitnami-labs/charts-syncer/api"
@@ -65,7 +64,7 @@ func GetChartLock(chartPath string) (*chart.Lock, error) {
6564
return lock, nil
6665
}
6766

68-
// GetChartDependencies returns the chart chart.Dependencies from a chart in tgz format.
67+
// GetChartDependencies returns the chart dependencies from a chart in tgz format.
6968
func GetChartDependencies(filepath string, name string) ([]*chart.Dependency, error) {
7069
// Create temporary working directory
7170
chartPath, err := ioutil.TempDir("", "charts-syncer")
@@ -112,9 +111,9 @@ func GetLockAPIVersion(chartPath string) (string, error) {
112111

113112
// BuildDependencies updates the chart dependencies and their repository references in the provided chart path
114113
//
115-
// It reads the lock file to download the versions from the target
116-
// chart repository (it assumes all charts are stored in a single repo).
117-
func BuildDependencies(chartPath string, r client.ChartsReader, sourceRepo, targetRepo *api.Repo, replaceDependencyRepo bool) error {
114+
// It reads the lock file to download the versions from the target chart repository
115+
func BuildDependencies(chartPath string, r client.ChartsReader, sourceRepo, targetRepo *api.Repo, t map[uint32]client.ChartsReaderWriter, syncTrusted, ignoreTrusted []*api.Repo) error {
116+
118117
// Build deps manually for OCI as helm does not support it yet
119118
if err := os.RemoveAll(path.Join(chartPath, "charts")); err != nil {
120119
return errors.Trace(err)
@@ -138,13 +137,14 @@ func BuildDependencies(chartPath string, r client.ChartsReader, sourceRepo, targ
138137
if apiVersion == "" {
139138
return nil
140139
}
140+
141141
switch apiVersion {
142142
case APIV1:
143-
if err := updateRequirementsFile(chartPath, lock, sourceRepo, targetRepo, replaceDependencyRepo); err != nil {
143+
if err := updateRequirementsFile(chartPath, lock, sourceRepo, targetRepo, syncTrusted, ignoreTrusted); err != nil {
144144
return errors.Trace(err)
145145
}
146146
case APIV2:
147-
if err := updateChartMetadataFile(chartPath, lock, sourceRepo, targetRepo, replaceDependencyRepo); err != nil {
147+
if err := updateChartMetadataFile(chartPath, lock, sourceRepo, targetRepo, syncTrusted, ignoreTrusted); err != nil {
148148
return errors.Trace(err)
149149
}
150150
default:
@@ -158,7 +158,22 @@ func BuildDependencies(chartPath string, r client.ChartsReader, sourceRepo, targ
158158
id := fmt.Sprintf("%s-%s", dep.Name, dep.Version)
159159
klog.V(4).Infof("Building %q chart dependency", id)
160160

161-
depTgz, err := r.Fetch(dep.Name, dep.Version)
161+
var repoClient client.ChartsReader = nil
162+
163+
depRepo := api.Repo{
164+
Url: dep.Repository,
165+
}
166+
167+
//if the repo is trusted and won't be synced - we download the dependency from it (source)
168+
if utils.ShouldIgnoreRepo(depRepo, syncTrusted, ignoreTrusted) {
169+
repoClient = t[utils.GetRepoLocationId(dep.Repository)]
170+
} else {
171+
//otherwise we download it from the destination repo
172+
repoClient = r
173+
}
174+
175+
depTgz, err := repoClient.Fetch(dep.Name, dep.Version)
176+
162177
if err != nil {
163178
klog.Warningf("Failed fetching %q chart. The dependencies processing will remain incomplete.", id)
164179
errs = multierror.Append(errs, errors.Annotatef(err, "fetching %q chart", id))
@@ -179,7 +194,7 @@ func BuildDependencies(chartPath string, r client.ChartsReader, sourceRepo, targ
179194

180195
// updateChartMetadataFile updates the dependencies in Chart.yaml
181196
// For helm v3 dependency management
182-
func updateChartMetadataFile(chartPath string, lock *chart.Lock, sourceRepo, targetRepo *api.Repo, replaceDependencyRepo bool) error {
197+
func updateChartMetadataFile(chartPath string, lock *chart.Lock, sourceRepo, targetRepo *api.Repo, syncTrusted, ignoreTrusted []*api.Repo) error {
183198
chartFile := path.Join(chartPath, ChartFilename)
184199
chartYamlContent, err := ioutil.ReadFile(chartFile)
185200
if err != nil {
@@ -191,8 +206,15 @@ func updateChartMetadataFile(chartPath string, lock *chart.Lock, sourceRepo, tar
191206
return errors.Annotatef(err, "error unmarshaling %s file", chartFile)
192207
}
193208
for _, dep := range chartMetadata.Dependencies {
194-
// Maybe there are dependencies from other chart repos. In this case we don't want to replace
195-
// the repository.
209+
// Maybe there are dependencies from other chart repos. We replace them or not depending on what we have in
210+
// source.ignoreTrustedRepos and target.syncTrustedRepos (the logic can be found in utils.ShouldIgnoreRepo)
211+
r := api.Repo{
212+
Url: dep.Repository,
213+
}
214+
215+
//ignore repo means don't replace it, don't ignore - means "replace it" - use negation to achieve it
216+
replaceDependencyRepo := !utils.ShouldIgnoreRepo(r, syncTrusted, ignoreTrusted)
217+
196218
if dep.Repository == sourceRepo.GetUrl() || replaceDependencyRepo {
197219
repoUrl, err := getDependencyRepoURL(targetRepo)
198220
if err != nil {
@@ -206,15 +228,15 @@ func updateChartMetadataFile(chartPath string, lock *chart.Lock, sourceRepo, tar
206228
if err := writeChartFile(dest, chartMetadata); err != nil {
207229
return errors.Trace(err)
208230
}
209-
if err := updateLockFile(chartPath, lock, chartMetadata.Dependencies, sourceRepo, targetRepo, false, replaceDependencyRepo); err != nil {
231+
if err := updateLockFile(chartPath, lock, chartMetadata.Dependencies, sourceRepo, targetRepo, false, syncTrusted, ignoreTrusted); err != nil {
210232
return errors.Trace(err)
211233
}
212234
return nil
213235
}
214236

215237
// updateRequirementsFile returns the full list of dependencies and the list of missing dependencies.
216238
// For helm v2 dependency management
217-
func updateRequirementsFile(chartPath string, lock *chart.Lock, sourceRepo, targetRepo *api.Repo, replaceDependencyRepo bool) error {
239+
func updateRequirementsFile(chartPath string, lock *chart.Lock, sourceRepo, targetRepo *api.Repo, syncTrusted, ignoreTrusted []*api.Repo) error {
218240
requirementsFile := path.Join(chartPath, RequirementsFilename)
219241
requirements, err := ioutil.ReadFile(requirementsFile)
220242
if err != nil {
@@ -227,8 +249,15 @@ func updateRequirementsFile(chartPath string, lock *chart.Lock, sourceRepo, targ
227249
return errors.Annotatef(err, "error unmarshaling %s file", requirementsFile)
228250
}
229251
for _, dep := range deps.Dependencies {
230-
// Maybe there are dependencies from other chart repos. In this case we don't want to replace
231-
// the repository.
252+
// Maybe there are dependencies from other chart repos. We replace them or not depending on what we have in
253+
// source.ignoreTrustedRepos and target.syncTrustedRepos (the logic can be found in utils.ShouldIgnoreRepo)
254+
r := api.Repo{
255+
Url: dep.Repository,
256+
}
257+
258+
//ignore repo means don't replace it, don't ignore - means "replace it" - use negation to achieve it
259+
replaceDependencyRepo := !utils.ShouldIgnoreRepo(r, syncTrusted, ignoreTrusted)
260+
232261
// For example, old charts pointing to helm/charts repo
233262
if dep.Repository == sourceRepo.GetUrl() || replaceDependencyRepo {
234263
repoUrl, err := getDependencyRepoURL(targetRepo)
@@ -243,15 +272,25 @@ func updateRequirementsFile(chartPath string, lock *chart.Lock, sourceRepo, targ
243272
if err := writeChartFile(dest, deps); err != nil {
244273
return errors.Trace(err)
245274
}
246-
if err := updateLockFile(chartPath, lock, deps.Dependencies, sourceRepo, targetRepo, true, replaceDependencyRepo); err != nil {
275+
if err := updateLockFile(chartPath, lock, deps.Dependencies, sourceRepo, targetRepo, true, syncTrusted, ignoreTrusted); err != nil {
247276
return errors.Trace(err)
248277
}
249278
return nil
250279
}
251280

252281
// updateLockFile updates the lock file with the new registry
253-
func updateLockFile(chartPath string, lock *chart.Lock, deps []*chart.Dependency, sourceRepo *api.Repo, targetRepo *api.Repo, legacyLockfile, replaceDependencyRepo bool) error {
282+
func updateLockFile(chartPath string, lock *chart.Lock, deps []*chart.Dependency, sourceRepo *api.Repo, targetRepo *api.Repo, legacyLockfile bool, syncTrusted, ignoreTrusted []*api.Repo) error {
254283
for _, dep := range lock.Dependencies {
284+
285+
// Maybe there are dependencies from other chart repos. We replace them or not depending on what we have in
286+
// source.ignoreTrustedRepos and target.syncTrustedRepos (the logic can be found in utils.ShouldIgnoreRepo)
287+
r := api.Repo{
288+
Url: dep.Repository,
289+
}
290+
291+
//ignore repo means don't replace it, don't ignore - means "replace it" - use negation to achieve it
292+
replaceDependencyRepo := !utils.ShouldIgnoreRepo(r, syncTrusted, ignoreTrusted)
293+
255294
if dep.Repository == sourceRepo.GetUrl() || replaceDependencyRepo {
256295
repoUrl, err := getDependencyRepoURL(targetRepo)
257296
if err != nil {

internal/chart/dependency_test.go

+7-2
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,10 @@ func TestUpdateRequirementsFile(t *testing.T) {
8989

9090
chartPath := newChartPath(t, "../../testdata/kafka-10.3.3.tgz", "kafka")
9191
requirementsFile := path.Join(chartPath, RequirementsFilename)
92-
if err := updateRequirementsFile(chartPath, lock, source.GetRepo(), target.GetRepo(), false); err != nil {
92+
93+
var ignoreTrusted, syncTrusted []*api.Repo
94+
95+
if err := updateRequirementsFile(chartPath, lock, source.GetRepo(), target.GetRepo(), syncTrusted, ignoreTrusted); err != nil {
9396
t.Fatal(err)
9497
}
9598

@@ -163,7 +166,9 @@ func TestUpdateChartMetadataFile(t *testing.T) {
163166
t.Fatal(err)
164167
}
165168

166-
if err := updateChartMetadataFile(chartPath, lock, source.GetRepo(), target.GetRepo(), false); err != nil {
169+
var ignoreTrusted, syncTrusted []*api.Repo
170+
171+
if err := updateChartMetadataFile(chartPath, lock, source.GetRepo(), target.GetRepo(), syncTrusted, ignoreTrusted); err != nil {
167172
t.Fatal(err)
168173
}
169174

internal/utils/utils.go

+41
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"crypto/sha1"
77
"crypto/tls"
88
"fmt"
9+
"hash/fnv"
910
"io"
1011
"io/ioutil"
1112
"net"
@@ -445,3 +446,43 @@ func FetchAndCache(name, version string, cache cache.Cacher, fopts ...FetchOptio
445446

446447
return cache.Path(id), nil
447448
}
449+
450+
func ShouldIgnoreRepo(repo api.Repo, syncTrusted, ignoreTrusted []*api.Repo) bool {
451+
452+
repoLocationId := GetRepoLocationId(GetRepoLocation(&repo))
453+
454+
for _, trRepo := range syncTrusted {
455+
if GetRepoLocationId(GetRepoLocation(trRepo)) == repoLocationId {
456+
return false
457+
}
458+
}
459+
460+
for _, ignoreTrRepo := range ignoreTrusted {
461+
if GetRepoLocationId(GetRepoLocation(ignoreTrRepo)) == repoLocationId {
462+
return true
463+
}
464+
}
465+
466+
return false
467+
}
468+
469+
// GetRepoLocationId returns a unique id for a repo based on the repo url or path
470+
func GetRepoLocationId(l string) uint32 {
471+
h := fnv.New32a()
472+
473+
//@todo trim whitespaces from the values used ?!
474+
h.Write([]byte(strings.ToLower(l)))
475+
476+
return h.Sum32()
477+
}
478+
479+
// GetRepoLocation returns the repo url or path
480+
func GetRepoLocation(repo *api.Repo) string {
481+
if repo.Url != "" {
482+
//remote repo
483+
return repo.Url
484+
}
485+
486+
//local repo
487+
return repo.Path
488+
}

pkg/syncer/index.go

+11-7
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package syncer
22

33
import (
44
"fmt"
5+
"github.com/bitnami-labs/charts-syncer/api"
56
"sort"
67
"time"
78

@@ -20,6 +21,7 @@ type Chart struct {
2021
Name string
2122
Version string
2223
Dependencies []string
24+
Repo api.Repo
2325

2426
TgzPath string
2527
}
@@ -161,7 +163,6 @@ func (s *Syncer) processVersion(name, version string, publishingThreshold time.T
161163
klog.V(5).Infof("Skipping %q chart: Already indexed", id)
162164
return nil
163165
}
164-
165166
if err := s.loadChart(name, version, "", false); err != nil {
166167
klog.Errorf("unable to load %q chart: %v", id, err)
167168
return err
@@ -199,15 +200,17 @@ func (s *Syncer) loadChart(name string, version string, repository string, isDep
199200
return nil
200201
}
201202

202-
//main source repo client
203-
client := s.cli.src
203+
var tgz string
204+
var err error
205+
206+
repoKey := utils.GetRepoLocationId(repository)
204207

205-
//in case of dependency - switch to deps client, but only if there is a valid entry for the current repo
206-
if isDep && len(s.cli.deps) > 0 && s.cli.deps[repository] != nil {
207-
client = s.cli.deps[repository]
208+
if isDep && s.cli.trusted[repoKey] != nil {
209+
tgz, err = s.cli.trusted[repoKey].Fetch(name, version)
210+
} else {
211+
tgz, err = s.cli.src.Fetch(name, version)
208212
}
209213

210-
tgz, err := client.Fetch(name, version)
211214
if err != nil {
212215
return errors.Trace(err)
213216
}
@@ -216,6 +219,7 @@ func (s *Syncer) loadChart(name string, version string, repository string, isDep
216219
Name: name,
217220
Version: version,
218221
TgzPath: tgz,
222+
Repo: api.Repo{Url: repository},
219223
}
220224

221225
if !s.skipDependencies {

0 commit comments

Comments
 (0)