mirror of https://github.com/artifacthub/hub.git
Check Helm HTTP repositories digest (#919)
Closes #917 Signed-off-by: Sergio Castaño Arteaga <tegioz@icloud.com>
This commit is contained in:
parent
d62375b7d3
commit
2566110c13
|
|
@ -70,7 +70,7 @@ func GetKindFromName(kind string) (RepositoryKind, error) {
|
|||
// HelmIndexLoader interface defines the methods a Helm index loader
|
||||
// implementation should provide.
|
||||
type HelmIndexLoader interface {
|
||||
LoadIndex(r *Repository) (*helmrepo.IndexFile, error)
|
||||
LoadIndex(r *Repository) (*helmrepo.IndexFile, string, error)
|
||||
}
|
||||
|
||||
// OLMRepositoryExporter describes the methods an OLMRepositoryExporter
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import (
|
|||
type HelmIndexLoader struct{}
|
||||
|
||||
// LoadIndex downloads and parses the index file of the provided repository.
|
||||
func (l *HelmIndexLoader) LoadIndex(r *hub.Repository) (*helmrepo.IndexFile, error) {
|
||||
func (l *HelmIndexLoader) LoadIndex(r *hub.Repository) (*helmrepo.IndexFile, string, error) {
|
||||
repoConfig := &helmrepo.Entry{
|
||||
Name: r.Name,
|
||||
URL: r.URL,
|
||||
|
|
@ -22,15 +22,15 @@ func (l *HelmIndexLoader) LoadIndex(r *hub.Repository) (*helmrepo.IndexFile, err
|
|||
getters := getter.All(&cli.EnvSettings{})
|
||||
chartRepository, err := helmrepo.NewChartRepository(repoConfig, getters)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, "", err
|
||||
}
|
||||
path, err := chartRepository.DownloadIndexFile()
|
||||
indexPath, err := chartRepository.DownloadIndexFile()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, "", err
|
||||
}
|
||||
indexFile, err := helmrepo.LoadIndexFile(path)
|
||||
indexFile, err := helmrepo.LoadIndexFile(indexPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, "", err
|
||||
}
|
||||
return indexFile, nil
|
||||
return indexFile, indexPath, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ package repo
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
|
@ -164,7 +166,7 @@ func (m *Manager) Add(ctx context.Context, orgName string, r *hub.Repository) er
|
|||
}
|
||||
}
|
||||
if r.Kind == hub.Helm && SchemeIsHTTP(u) {
|
||||
if _, err := m.helmIndexLoader.LoadIndex(r); err != nil {
|
||||
if _, _, err := m.helmIndexLoader.LoadIndex(r); err != nil {
|
||||
return fmt.Errorf("%w: %s: %s", hub.ErrInvalidInput, "invalid url", err.Error())
|
||||
}
|
||||
}
|
||||
|
|
@ -481,16 +483,40 @@ func (m *Manager) GetOwnedByUserJSON(ctx context.Context, includeCredentials boo
|
|||
return util.DBQueryJSON(ctx, m.db, getUserReposDBQ, userID, includeCredentials)
|
||||
}
|
||||
|
||||
// GetRemoteDigest gets the repository's digest available in the remote. In the
|
||||
// case of git based repositories, the digest corresponds to the hash of the
|
||||
// last commit. In OCI based repositories, it is the digest of the image the
|
||||
// repository url points to.
|
||||
// GetRemoteDigest gets the repository's digest available in the remote.
|
||||
func (m *Manager) GetRemoteDigest(ctx context.Context, r *hub.Repository) (string, error) {
|
||||
var digest string
|
||||
|
||||
u, _ := url.Parse(r.URL)
|
||||
|
||||
switch {
|
||||
case r.Kind == hub.Helm && SchemeIsHTTP(u):
|
||||
// Digest is obtained hashing the repository index.yaml file
|
||||
_, indexPath, err := m.helmIndexLoader.LoadIndex(r)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
indexBytes, err := ioutil.ReadFile(indexPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
hash := sha256.Sum256(indexBytes)
|
||||
digest = hex.EncodeToString(hash[:])
|
||||
|
||||
case r.Kind == hub.OLM && u.Scheme == "oci":
|
||||
// Digest is obtained from the index image digest
|
||||
refName := strings.TrimPrefix(r.URL, hub.RepositoryOCIPrefix)
|
||||
ref, err := name.ParseReference(refName)
|
||||
if err != nil {
|
||||
return digest, err
|
||||
}
|
||||
desc, err := remote.Head(ref)
|
||||
if err != nil {
|
||||
return digest, err
|
||||
}
|
||||
digest = desc.Digest.String()
|
||||
|
||||
case SchemeIsHTTP(u) && u.Host == "github.com":
|
||||
// Digest is obtained from the last commit in the repository
|
||||
pathParts := strings.Split(strings.TrimPrefix(u.Path, "/"), "/")
|
||||
if len(pathParts) < 2 {
|
||||
break
|
||||
|
|
@ -510,17 +536,6 @@ func (m *Manager) GetRemoteDigest(ctx context.Context, r *hub.Repository) (strin
|
|||
if len(commits) == 1 {
|
||||
digest = *commits[0].SHA
|
||||
}
|
||||
case u.Scheme == "oci":
|
||||
refName := strings.TrimPrefix(r.URL, hub.RepositoryOCIPrefix)
|
||||
ref, err := name.ParseReference(refName)
|
||||
if err != nil {
|
||||
return digest, err
|
||||
}
|
||||
desc, err := remote.Head(ref)
|
||||
if err != nil {
|
||||
return digest, err
|
||||
}
|
||||
digest = desc.Digest.String()
|
||||
}
|
||||
|
||||
return digest, nil
|
||||
|
|
@ -634,7 +649,7 @@ func (m *Manager) Update(ctx context.Context, r *hub.Repository) error {
|
|||
}
|
||||
}
|
||||
if r.Kind == hub.Helm && SchemeIsHTTP(u) {
|
||||
if _, err := m.helmIndexLoader.LoadIndex(r); err != nil {
|
||||
if _, _, err := m.helmIndexLoader.LoadIndex(r); err != nil {
|
||||
return fmt.Errorf("%w: %s: %s", hub.ErrInvalidInput, "invalid url", err.Error())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,10 +22,7 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var (
|
||||
cfg = viper.New()
|
||||
errFake = errors.New("fake error for tests")
|
||||
)
|
||||
var cfg = viper.New()
|
||||
|
||||
func TestAdd(t *testing.T) {
|
||||
ctx := context.WithValue(context.Background(), hub.UserIDKey, "userID")
|
||||
|
|
@ -179,7 +176,7 @@ func TestAdd(t *testing.T) {
|
|||
t.Run(tc.errMsg, func(t *testing.T) {
|
||||
l := &HelmIndexLoaderMock{}
|
||||
if tc.lErr != nil {
|
||||
l.On("LoadIndex", mock.Anything).Return(nil, tc.lErr)
|
||||
l.On("LoadIndex", tc.r).Return(nil, "", tc.lErr)
|
||||
}
|
||||
m := NewManager(cfg, nil, nil, WithHelmIndexLoader(l))
|
||||
|
||||
|
|
@ -205,7 +202,7 @@ func TestAdd(t *testing.T) {
|
|||
Action: hub.AddOrganizationRepository,
|
||||
}).Return(tests.ErrFake)
|
||||
l := &HelmIndexLoaderMock{}
|
||||
l.On("LoadIndex", r).Return(nil, nil)
|
||||
l.On("LoadIndex", r).Return(nil, "", nil)
|
||||
m := NewManager(cfg, nil, az, WithHelmIndexLoader(l))
|
||||
|
||||
err := m.Add(ctx, "orgName", r)
|
||||
|
|
@ -252,7 +249,7 @@ func TestAdd(t *testing.T) {
|
|||
Action: hub.AddOrganizationRepository,
|
||||
}).Return(nil)
|
||||
l := &HelmIndexLoaderMock{}
|
||||
l.On("LoadIndex", tc.r).Return(nil, nil)
|
||||
l.On("LoadIndex", tc.r).Return(nil, "", nil)
|
||||
m := NewManager(cfg, db, az, WithHelmIndexLoader(l))
|
||||
|
||||
err := m.Add(ctx, "orgName", tc.r)
|
||||
|
|
@ -298,7 +295,7 @@ func TestAdd(t *testing.T) {
|
|||
}).Return(nil)
|
||||
l := &HelmIndexLoaderMock{}
|
||||
if tc.r.Kind == hub.Helm {
|
||||
l.On("LoadIndex", tc.r).Return(nil, nil)
|
||||
l.On("LoadIndex", tc.r).Return(nil, "", nil)
|
||||
}
|
||||
m := NewManager(cfg, db, az, WithHelmIndexLoader(l))
|
||||
|
||||
|
|
@ -524,11 +521,11 @@ func TestClaimOwnership(t *testing.T) {
|
|||
rc := &ClonerMock{}
|
||||
var r *hub.Repository
|
||||
_ = json.Unmarshal(opaRepoJSON, &r)
|
||||
rc.On("CloneRepository", ctx, r).Return("", "", errFake)
|
||||
rc.On("CloneRepository", ctx, r).Return("", "", tests.ErrFake)
|
||||
m := NewManager(cfg, db, nil, withRepositoryCloner(rc))
|
||||
|
||||
err := m.ClaimOwnership(ctx, "repo1", org)
|
||||
assert.Equal(t, errFake, err)
|
||||
assert.Equal(t, tests.ErrFake, err)
|
||||
db.AssertExpectations(t)
|
||||
})
|
||||
|
||||
|
|
@ -1141,6 +1138,50 @@ func TestGetOwnedByUserJSON(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestGetRemoteDigest(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
helmHTTP := &hub.Repository{
|
||||
Kind: hub.Helm,
|
||||
Name: "repo1",
|
||||
URL: "https://myrepo.url",
|
||||
}
|
||||
|
||||
t.Run("helm-http: error loading index", func(t *testing.T) {
|
||||
l := &HelmIndexLoaderMock{}
|
||||
l.On("LoadIndex", helmHTTP).Return(nil, "", tests.ErrFake)
|
||||
m := NewManager(cfg, nil, nil, WithHelmIndexLoader(l))
|
||||
|
||||
digest, err := m.GetRemoteDigest(ctx, helmHTTP)
|
||||
assert.Empty(t, digest)
|
||||
assert.Equal(t, tests.ErrFake, err)
|
||||
})
|
||||
|
||||
t.Run("helm-http: error reading index file", func(t *testing.T) {
|
||||
l := &HelmIndexLoaderMock{}
|
||||
l.On("LoadIndex", helmHTTP).Return(nil, "invalid-path", nil)
|
||||
m := NewManager(cfg, nil, nil, WithHelmIndexLoader(l))
|
||||
|
||||
digest, err := m.GetRemoteDigest(ctx, helmHTTP)
|
||||
assert.Empty(t, digest)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("helm-http: success", func(t *testing.T) {
|
||||
f, err := ioutil.TempFile("", "")
|
||||
require.NoError(t, err)
|
||||
_, _ = f.Write([]byte("indexFileContent"))
|
||||
require.NoError(t, err)
|
||||
|
||||
l := &HelmIndexLoaderMock{}
|
||||
l.On("LoadIndex", helmHTTP).Return(nil, f.Name(), nil)
|
||||
m := NewManager(cfg, nil, nil, WithHelmIndexLoader(l))
|
||||
|
||||
digest, err := m.GetRemoteDigest(ctx, helmHTTP)
|
||||
assert.Equal(t, "a1cbe8e02116f43084632fbf313c4ed02772f93af327bbc16989a30bc04ddc89", digest)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSetLastTrackingResults(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
repoID := "00000000-0000-0000-0000-000000000001"
|
||||
|
|
@ -1413,7 +1454,7 @@ func TestUpdate(t *testing.T) {
|
|||
t.Run(tc.errMsg, func(t *testing.T) {
|
||||
l := &HelmIndexLoaderMock{}
|
||||
if tc.lErr != nil {
|
||||
l.On("LoadIndex", mock.Anything).Return(nil, tc.lErr)
|
||||
l.On("LoadIndex", tc.r).Return(nil, "", tc.lErr)
|
||||
}
|
||||
m := NewManager(cfg, nil, nil, WithHelmIndexLoader(l))
|
||||
|
||||
|
|
@ -1446,7 +1487,7 @@ func TestUpdate(t *testing.T) {
|
|||
Action: hub.UpdateOrganizationRepository,
|
||||
}).Return(tests.ErrFake)
|
||||
l := &HelmIndexLoaderMock{}
|
||||
l.On("LoadIndex", r).Return(nil, nil)
|
||||
l.On("LoadIndex", r).Return(nil, "", nil)
|
||||
m := NewManager(cfg, db, az, WithHelmIndexLoader(l))
|
||||
|
||||
err := m.Update(ctx, r)
|
||||
|
|
@ -1502,7 +1543,7 @@ func TestUpdate(t *testing.T) {
|
|||
}).Return(nil)
|
||||
|
||||
l := &HelmIndexLoaderMock{}
|
||||
l.On("LoadIndex", tc.r).Return(nil, nil)
|
||||
l.On("LoadIndex", tc.r).Return(nil, "", nil)
|
||||
m := NewManager(cfg, db, az, WithHelmIndexLoader(l))
|
||||
|
||||
err := m.Update(ctx, tc.r)
|
||||
|
|
@ -1544,7 +1585,7 @@ func TestUpdate(t *testing.T) {
|
|||
db.On("Exec", ctx, updateRepoDBQ, "userID", mock.Anything).Return(nil)
|
||||
l := &HelmIndexLoaderMock{}
|
||||
if tc.r.Kind == hub.Helm {
|
||||
l.On("LoadIndex", tc.r).Return(nil, nil)
|
||||
l.On("LoadIndex", tc.r).Return(nil, "", nil)
|
||||
}
|
||||
m := NewManager(cfg, db, nil, WithHelmIndexLoader(l))
|
||||
|
||||
|
|
|
|||
|
|
@ -26,10 +26,10 @@ type HelmIndexLoaderMock struct {
|
|||
}
|
||||
|
||||
// LoadIndex implements the HelmIndexLoader interface.
|
||||
func (m *HelmIndexLoaderMock) LoadIndex(r *hub.Repository) (*repo.IndexFile, error) {
|
||||
func (m *HelmIndexLoaderMock) LoadIndex(r *hub.Repository) (*repo.IndexFile, string, error) {
|
||||
args := m.Called(r)
|
||||
indexFile, _ := args.Get(0).(*repo.IndexFile)
|
||||
return indexFile, args.Error(1)
|
||||
return indexFile, args.String(1), args.Error(2)
|
||||
}
|
||||
|
||||
// ManagerMock is a mock implementation of the RepositoryManager interface.
|
||||
|
|
|
|||
|
|
@ -197,7 +197,7 @@ func (t *Tracker) getCharts() (map[string][]*helmrepo.ChartVersion, error) {
|
|||
case "http", "https":
|
||||
// Load repository index file
|
||||
t.logger.Debug().Msg("loading repository index file")
|
||||
indexFile, err := t.svc.Il.LoadIndex(t.r)
|
||||
indexFile, _, err := t.svc.Il.LoadIndex(t.r)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error loading repository index file: %w", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ func TestTracker(t *testing.T) {
|
|||
}
|
||||
tw := newTrackerWrapper(r)
|
||||
tw.rm.On("GetPackagesDigest", tw.ctx, r.RepositoryID).Return(nil, nil)
|
||||
tw.il.On("LoadIndex", r).Return(nil, tests.ErrFake)
|
||||
tw.il.On("LoadIndex", r).Return(nil, "", tests.ErrFake)
|
||||
|
||||
// Run tracker and check expectations
|
||||
err := tw.t.Track()
|
||||
|
|
@ -314,7 +314,7 @@ func TestTracker(t *testing.T) {
|
|||
tw := newTrackerWrapper(tc.r)
|
||||
tw.rm.On("GetPackagesDigest", tw.ctx, tc.r.RepositoryID).
|
||||
Return(tc.packagesDigest[tc.r.RepositoryID], nil)
|
||||
tw.il.On("LoadIndex", tc.r).Return(tc.indexFile[tc.r.RepositoryID], nil)
|
||||
tw.il.On("LoadIndex", tc.r).Return(tc.indexFile[tc.r.RepositoryID], "", nil)
|
||||
u, _ := url.Parse(tc.r.URL)
|
||||
u.Path = path.Join(u.Path, hub.RepositoryMetadataFile)
|
||||
tw.rm.On("GetMetadata", u.String()).Return(&hub.RepositoryMetadata{
|
||||
|
|
|
|||
Loading…
Reference in New Issue