Add artifact to Git and Helm repos status
- create index.yaml symlink for Helm repos - set symlink URL in status
This commit is contained in:
parent
461c015273
commit
2bbcd91544
|
@ -42,3 +42,61 @@ func NotReadyCondition(reason, message string) sourcev1.SourceCondition {
|
|||
Message: message,
|
||||
}
|
||||
}
|
||||
|
||||
func ReadyGitRepository(repository sourcev1.GitRepository, artifact sourcev1.Artifact, url, reason, message string) sourcev1.GitRepository {
|
||||
repository.Status.Conditions = []sourcev1.SourceCondition{ReadyCondition(reason, message)}
|
||||
repository.Status.URL = url
|
||||
|
||||
if repository.Status.Artifact != nil {
|
||||
if repository.Status.Artifact.Path != artifact.Path {
|
||||
repository.Status.Artifact = &artifact
|
||||
}
|
||||
} else {
|
||||
repository.Status.Artifact = &artifact
|
||||
}
|
||||
|
||||
return repository
|
||||
}
|
||||
|
||||
func NotReadyGitRepository(repository sourcev1.GitRepository, reason, message string) sourcev1.GitRepository {
|
||||
repository.Status.Conditions = []sourcev1.SourceCondition{NotReadyCondition(reason, message)}
|
||||
return repository
|
||||
}
|
||||
|
||||
func GitRepositoryReadyMessage(repository sourcev1.GitRepository) string {
|
||||
for _, condition := range repository.Status.Conditions {
|
||||
if condition.Type == sourcev1.ReadyCondition {
|
||||
return condition.Message
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func ReadyHelmRepository(repository sourcev1.HelmRepository, artifact sourcev1.Artifact, url, reason, message string) sourcev1.HelmRepository {
|
||||
repository.Status.Conditions = []sourcev1.SourceCondition{ReadyCondition(reason, message)}
|
||||
repository.Status.URL = url
|
||||
|
||||
if repository.Status.Artifact != nil {
|
||||
if repository.Status.Artifact.Path != artifact.Path {
|
||||
repository.Status.Artifact = &artifact
|
||||
}
|
||||
} else {
|
||||
repository.Status.Artifact = &artifact
|
||||
}
|
||||
|
||||
return repository
|
||||
}
|
||||
|
||||
func NotReadyHelmRepository(repository sourcev1.HelmRepository, reason, message string) sourcev1.HelmRepository {
|
||||
repository.Status.Conditions = []sourcev1.SourceCondition{NotReadyCondition(reason, message)}
|
||||
return repository
|
||||
}
|
||||
|
||||
func HelmRepositoryReadyMessage(repository sourcev1.HelmRepository) string {
|
||||
for _, condition := range repository.Status.Conditions {
|
||||
if condition.Type == sourcev1.ReadyCondition {
|
||||
return condition.Message
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
|
|
@ -83,28 +83,19 @@ func (r *GitRepositoryReconciler) Reconcile(req ctrl.Request) (ctrl.Result, erro
|
|||
r.gc(repo)
|
||||
|
||||
// try git clone
|
||||
readyCondition, artifacts, err := r.sync(repo)
|
||||
syncedRepo, err := r.sync(*repo.DeepCopy())
|
||||
if err != nil {
|
||||
log.Info("Repository sync failed", "error", err.Error())
|
||||
} else {
|
||||
// update artifacts if commit hash changed
|
||||
if repo.Status.Artifact != artifacts {
|
||||
timeNew := metav1.Now()
|
||||
repo.Status.LastUpdateTime = &timeNew
|
||||
repo.Status.Artifact = artifacts
|
||||
}
|
||||
log.Info("Repository sync succeeded", "msg", readyCondition.Message)
|
||||
}
|
||||
|
||||
// update status
|
||||
readyCondition.LastTransitionTime = metav1.Now()
|
||||
repo.Status.Conditions = []sourcev1.SourceCondition{readyCondition}
|
||||
|
||||
if err := r.Status().Update(ctx, &repo); err != nil {
|
||||
if err := r.Status().Update(ctx, &syncedRepo); err != nil {
|
||||
log.Error(err, "unable to update GitRepository status")
|
||||
return result, err
|
||||
}
|
||||
|
||||
log.Info("Repository sync succeeded", "msg", GitRepositoryReadyMessage(syncedRepo))
|
||||
|
||||
// requeue repository
|
||||
return result, nil
|
||||
}
|
||||
|
@ -116,7 +107,7 @@ func (r *GitRepositoryReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
|||
WithEventFilter(predicate.Funcs{
|
||||
DeleteFunc: func(e event.DeleteEvent) bool {
|
||||
// delete artifacts
|
||||
artifact := r.Storage.ArtifactFor(r.Kind, e.Meta, "dummy")
|
||||
artifact := r.Storage.ArtifactFor(r.Kind, e.Meta, "dummy", "")
|
||||
if err := r.Storage.RemoveAll(artifact); err != nil {
|
||||
r.Log.Error(err, "unable to delete artifacts",
|
||||
r.Kind, fmt.Sprintf("%s/%s", e.Meta.GetNamespace(), e.Meta.GetName()))
|
||||
|
@ -130,9 +121,10 @@ func (r *GitRepositoryReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
|||
Complete(r)
|
||||
}
|
||||
|
||||
func (r *GitRepositoryReconciler) sync(repository sourcev1.GitRepository) (sourcev1.SourceCondition, string, error) {
|
||||
func (r *GitRepositoryReconciler) sync(repository sourcev1.GitRepository) (sourcev1.GitRepository, error) {
|
||||
// set defaults: master branch, no tags fetching, max two commits
|
||||
branch := "master"
|
||||
revision := ""
|
||||
tagMode := git.NoTags
|
||||
depth := 2
|
||||
|
||||
|
@ -159,21 +151,21 @@ func (r *GitRepositoryReconciler) sync(repository sourcev1.GitRepository) (sourc
|
|||
tmpSSH, err := ioutil.TempDir("", repository.Name)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("tmp dir error: %w", err)
|
||||
return NotReadyCondition(sourcev1.StorageOperationFailedReason, err.Error()), "", err
|
||||
return NotReadyGitRepository(repository, sourcev1.StorageOperationFailedReason, err.Error()), err
|
||||
}
|
||||
defer os.RemoveAll(tmpSSH)
|
||||
|
||||
auth, err := r.auth(repository, tmpSSH)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("auth error: %w", err)
|
||||
return NotReadyCondition(sourcev1.AuthenticationFailedReason, err.Error()), "", err
|
||||
return NotReadyGitRepository(repository, sourcev1.StorageOperationFailedReason, err.Error()), err
|
||||
}
|
||||
|
||||
// create tmp dir for the Git clone
|
||||
tmpGit, err := ioutil.TempDir("", repository.Name)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("tmp dir error: %w", err)
|
||||
return NotReadyCondition(sourcev1.StorageOperationFailedReason, err.Error()), "", err
|
||||
return NotReadyGitRepository(repository, sourcev1.StorageOperationFailedReason, err.Error()), err
|
||||
}
|
||||
defer os.RemoveAll(tmpGit)
|
||||
|
||||
|
@ -192,7 +184,7 @@ func (r *GitRepositoryReconciler) sync(repository sourcev1.GitRepository) (sourc
|
|||
})
|
||||
if err != nil {
|
||||
err = fmt.Errorf("git clone error: %w", err)
|
||||
return NotReadyCondition(sourcev1.GitOperationFailedReason, err.Error()), "", err
|
||||
return NotReadyGitRepository(repository, sourcev1.GitOperationFailedReason, err.Error()), err
|
||||
}
|
||||
|
||||
// checkout commit or tag
|
||||
|
@ -201,7 +193,7 @@ func (r *GitRepositoryReconciler) sync(repository sourcev1.GitRepository) (sourc
|
|||
w, err := repo.Worktree()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("git worktree error: %w", err)
|
||||
return NotReadyCondition(sourcev1.GitOperationFailedReason, err.Error()), "", err
|
||||
return NotReadyGitRepository(repository, sourcev1.GitOperationFailedReason, err.Error()), err
|
||||
}
|
||||
|
||||
err = w.Checkout(&git.CheckoutOptions{
|
||||
|
@ -210,19 +202,19 @@ func (r *GitRepositoryReconciler) sync(repository sourcev1.GitRepository) (sourc
|
|||
})
|
||||
if err != nil {
|
||||
err = fmt.Errorf("git checkout %s for %s error: %w", commit, branch, err)
|
||||
return NotReadyCondition(sourcev1.GitOperationFailedReason, err.Error()), "", err
|
||||
return NotReadyGitRepository(repository, sourcev1.GitOperationFailedReason, err.Error()), err
|
||||
}
|
||||
} else if exp := repository.Spec.Reference.SemVer; exp != "" {
|
||||
rng, err := semver.ParseRange(exp)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("semver parse range error: %w", err)
|
||||
return NotReadyCondition(sourcev1.GitOperationFailedReason, err.Error()), "", err
|
||||
return NotReadyGitRepository(repository, sourcev1.GitOperationFailedReason, err.Error()), err
|
||||
}
|
||||
|
||||
repoTags, err := repo.Tags()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("git list tags error: %w", err)
|
||||
return NotReadyCondition(sourcev1.GitOperationFailedReason, err.Error()), "", err
|
||||
return NotReadyGitRepository(repository, sourcev1.GitOperationFailedReason, err.Error()), err
|
||||
}
|
||||
|
||||
tags := make(map[string]string)
|
||||
|
@ -246,11 +238,12 @@ func (r *GitRepositoryReconciler) sync(repository sourcev1.GitRepository) (sourc
|
|||
v := svers[len(svers)-1]
|
||||
t := svTags[v.String()]
|
||||
commit := tags[t]
|
||||
revision = fmt.Sprintf("%s/%s", t, commit)
|
||||
|
||||
w, err := repo.Worktree()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("git worktree error: %w", err)
|
||||
return NotReadyCondition(sourcev1.GitOperationFailedReason, err.Error()), "", err
|
||||
return NotReadyGitRepository(repository, sourcev1.GitOperationFailedReason, err.Error()), err
|
||||
}
|
||||
|
||||
err = w.Checkout(&git.CheckoutOptions{
|
||||
|
@ -258,11 +251,11 @@ func (r *GitRepositoryReconciler) sync(repository sourcev1.GitRepository) (sourc
|
|||
})
|
||||
if err != nil {
|
||||
err = fmt.Errorf("git checkout error: %w", err)
|
||||
return NotReadyCondition(sourcev1.GitOperationFailedReason, err.Error()), "", err
|
||||
return NotReadyGitRepository(repository, sourcev1.GitOperationFailedReason, err.Error()), err
|
||||
}
|
||||
} else {
|
||||
err = fmt.Errorf("no match found for semver: %s", repository.Spec.Reference.SemVer)
|
||||
return NotReadyCondition(sourcev1.GitOperationFailedReason, err.Error()), "", err
|
||||
return NotReadyGitRepository(repository, sourcev1.GitOperationFailedReason, err.Error()), err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -271,24 +264,28 @@ func (r *GitRepositoryReconciler) sync(repository sourcev1.GitRepository) (sourc
|
|||
ref, err := repo.Head()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("git resolve HEAD error: %w", err)
|
||||
return NotReadyCondition(sourcev1.GitOperationFailedReason, err.Error()), "", err
|
||||
return NotReadyGitRepository(repository, sourcev1.GitOperationFailedReason, err.Error()), err
|
||||
}
|
||||
|
||||
if revision == "" {
|
||||
revision = fmt.Sprintf("%s/%s", branch, ref.Hash().String())
|
||||
}
|
||||
|
||||
artifact := r.Storage.ArtifactFor(r.Kind, repository.ObjectMeta.GetObjectMeta(),
|
||||
fmt.Sprintf("%s.tar.gz", ref.Hash().String()))
|
||||
fmt.Sprintf("%s.tar.gz", ref.Hash().String()), revision)
|
||||
|
||||
// create artifact dir
|
||||
err = r.Storage.MkdirAll(artifact)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("mkdir dir error: %w", err)
|
||||
return NotReadyCondition(sourcev1.StorageOperationFailedReason, err.Error()), "", err
|
||||
return NotReadyGitRepository(repository, sourcev1.StorageOperationFailedReason, err.Error()), err
|
||||
}
|
||||
|
||||
// acquire lock
|
||||
unlock, err := r.Storage.Lock(artifact)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("unable to acquire lock: %w", err)
|
||||
return NotReadyCondition(sourcev1.StorageOperationFailedReason, err.Error()), "", err
|
||||
return NotReadyGitRepository(repository, sourcev1.StorageOperationFailedReason, err.Error()), err
|
||||
}
|
||||
defer unlock()
|
||||
|
||||
|
@ -296,26 +293,24 @@ func (r *GitRepositoryReconciler) sync(repository sourcev1.GitRepository) (sourc
|
|||
err = r.Storage.Archive(artifact, tmpGit, "")
|
||||
if err != nil {
|
||||
err = fmt.Errorf("storage archive error: %w", err)
|
||||
return NotReadyCondition(sourcev1.StorageOperationFailedReason, err.Error()), "", err
|
||||
return NotReadyGitRepository(repository, sourcev1.StorageOperationFailedReason, err.Error()), err
|
||||
}
|
||||
|
||||
// update latest symlink
|
||||
err = r.Storage.Symlink(artifact, "latest.tar.gz")
|
||||
url, err := r.Storage.Symlink(artifact, "latest.tar.gz")
|
||||
if err != nil {
|
||||
err = fmt.Errorf("storage lock error: %w", err)
|
||||
return NotReadyCondition(sourcev1.StorageOperationFailedReason, err.Error()), "", err
|
||||
return NotReadyGitRepository(repository, sourcev1.StorageOperationFailedReason, err.Error()), err
|
||||
}
|
||||
|
||||
message := fmt.Sprintf("Artifact is available at: %s", artifact.Path)
|
||||
return ReadyCondition(sourcev1.GitOperationSucceedReason, message), artifact.URL, nil
|
||||
return ReadyGitRepository(repository, artifact, url, sourcev1.GitOperationSucceedReason, message), nil
|
||||
}
|
||||
|
||||
func (r *GitRepositoryReconciler) shouldResetStatus(repository sourcev1.GitRepository) (bool, sourcev1.GitRepositoryStatus) {
|
||||
resetStatus := false
|
||||
if repository.Status.Artifact != "" {
|
||||
parts := strings.Split(repository.Status.Artifact, "/")
|
||||
artifact := r.Storage.ArtifactFor(r.Kind, repository.ObjectMeta.GetObjectMeta(), parts[len(parts)-1])
|
||||
if !r.Storage.ArtifactExist(artifact) {
|
||||
if repository.Status.Artifact != nil {
|
||||
if !r.Storage.ArtifactExist(*repository.Status.Artifact) {
|
||||
resetStatus = true
|
||||
}
|
||||
}
|
||||
|
@ -338,10 +333,8 @@ func (r *GitRepositoryReconciler) shouldResetStatus(repository sourcev1.GitRepos
|
|||
}
|
||||
|
||||
func (r *GitRepositoryReconciler) gc(repository sourcev1.GitRepository) {
|
||||
if repository.Status.Artifact != "" {
|
||||
parts := strings.Split(repository.Status.Artifact, "/")
|
||||
artifact := r.Storage.ArtifactFor(r.Kind, repository.ObjectMeta.GetObjectMeta(), parts[len(parts)-1])
|
||||
if err := r.Storage.RemoveAllButCurrent(artifact); err != nil {
|
||||
if repository.Status.Artifact != nil {
|
||||
if err := r.Storage.RemoveAllButCurrent(*repository.Status.Artifact); err != nil {
|
||||
r.Log.Info("Artifacts GC failed", "error", err)
|
||||
}
|
||||
}
|
||||
|
@ -370,9 +363,13 @@ func (r *GitRepositoryReconciler) auth(repository sourcev1.GitRepository, tmp st
|
|||
auth := &http.BasicAuth{}
|
||||
if username, ok := credentials["username"]; ok {
|
||||
auth.Username = string(username)
|
||||
} else {
|
||||
return nil, fmt.Errorf("%s secret does not contain username", repository.Spec.SecretRef.Name)
|
||||
}
|
||||
if password, ok := credentials["password"]; ok {
|
||||
auth.Password = string(password)
|
||||
} else {
|
||||
return nil, fmt.Errorf("%s secret does not contain password", repository.Spec.SecretRef.Name)
|
||||
}
|
||||
|
||||
if auth.Username == "" || auth.Password == "" {
|
||||
|
|
|
@ -22,7 +22,6 @@ import (
|
|||
"io/ioutil"
|
||||
"net/url"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
|
@ -57,7 +56,7 @@ func (r *HelmRepositoryReconciler) Reconcile(req ctrl.Request) (ctrl.Result, err
|
|||
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
||||
defer cancel()
|
||||
|
||||
log := r.Log.WithValues("helmrepository", req.NamespacedName)
|
||||
log := r.Log.WithValues(r.Kind, req.NamespacedName)
|
||||
|
||||
var repository sourcev1.HelmRepository
|
||||
if err := r.Get(ctx, req.NamespacedName, &repository); err != nil {
|
||||
|
@ -80,28 +79,19 @@ func (r *HelmRepositoryReconciler) Reconcile(req ctrl.Request) (ctrl.Result, err
|
|||
r.gc(repository)
|
||||
|
||||
// try to download index
|
||||
readyCondition, artifact, err := r.index(repository)
|
||||
syncedRepo, err := r.sync(*repository.DeepCopy())
|
||||
if err != nil {
|
||||
log.Info("Helm repository index failed", "error", err.Error())
|
||||
} else {
|
||||
// update artifact if path changed
|
||||
if repository.Status.Artifact != artifact {
|
||||
timeNew := metav1.Now()
|
||||
repository.Status.LastUpdateTime = &timeNew
|
||||
repository.Status.Artifact = artifact
|
||||
}
|
||||
log.Info("Helm repository index succeeded", "msg", readyCondition.Message)
|
||||
}
|
||||
|
||||
// update status
|
||||
readyCondition.LastTransitionTime = metav1.Now()
|
||||
repository.Status.Conditions = []sourcev1.SourceCondition{readyCondition}
|
||||
|
||||
if err := r.Status().Update(ctx, &repository); err != nil {
|
||||
if err := r.Status().Update(ctx, &syncedRepo); err != nil {
|
||||
log.Error(err, "unable to update HelmRepository status")
|
||||
return result, err
|
||||
}
|
||||
|
||||
log.Info("Repository sync succeeded", "msg", HelmRepositoryReadyMessage(syncedRepo))
|
||||
|
||||
// requeue repository
|
||||
return result, nil
|
||||
}
|
||||
|
@ -113,7 +103,7 @@ func (r *HelmRepositoryReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
|||
WithEventFilter(predicate.Funcs{
|
||||
DeleteFunc: func(e event.DeleteEvent) bool {
|
||||
// delete artifacts
|
||||
artifact := r.Storage.ArtifactFor(r.Kind, e.Meta, "dummy")
|
||||
artifact := r.Storage.ArtifactFor(r.Kind, e.Meta, "", "")
|
||||
if err := r.Storage.RemoveAll(artifact); err != nil {
|
||||
r.Log.Error(err, "unable to delete artifacts",
|
||||
r.Kind, fmt.Sprintf("%s/%s", e.Meta.GetNamespace(), e.Meta.GetName()))
|
||||
|
@ -127,15 +117,15 @@ func (r *HelmRepositoryReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
|||
Complete(r)
|
||||
}
|
||||
|
||||
func (r *HelmRepositoryReconciler) index(repository sourcev1.HelmRepository) (sourcev1.SourceCondition, string, error) {
|
||||
func (r *HelmRepositoryReconciler) sync(repository sourcev1.HelmRepository) (sourcev1.HelmRepository, error) {
|
||||
u, err := url.Parse(repository.Spec.URL)
|
||||
if err != nil {
|
||||
return NotReadyCondition(sourcev1.URLInvalidReason, err.Error()), "", err
|
||||
return NotReadyHelmRepository(repository, sourcev1.URLInvalidReason, err.Error()), err
|
||||
}
|
||||
|
||||
c, err := r.Getters.ByScheme(u.Scheme)
|
||||
if err != nil {
|
||||
return NotReadyCondition(sourcev1.URLInvalidReason, err.Error()), "", err
|
||||
return NotReadyHelmRepository(repository, sourcev1.URLInvalidReason, err.Error()), err
|
||||
}
|
||||
|
||||
u.RawPath = path.Join(u.RawPath, "index.yaml")
|
||||
|
@ -145,40 +135,40 @@ func (r *HelmRepositoryReconciler) index(repository sourcev1.HelmRepository) (so
|
|||
// TODO(hidde): add authentication config
|
||||
res, err := c.Get(indexURL, getter.WithURL(repository.Spec.URL))
|
||||
if err != nil {
|
||||
return NotReadyCondition(sourcev1.IndexationFailedReason, err.Error()), "", err
|
||||
return NotReadyHelmRepository(repository, sourcev1.IndexationFailedReason, err.Error()), err
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadAll(res)
|
||||
if err != nil {
|
||||
return NotReadyCondition(sourcev1.IndexationFailedReason, err.Error()), "", err
|
||||
return NotReadyHelmRepository(repository, sourcev1.IndexationFailedReason, err.Error()), err
|
||||
}
|
||||
|
||||
i := &repo.IndexFile{}
|
||||
if err := yaml.Unmarshal(data, i); err != nil {
|
||||
return NotReadyCondition(sourcev1.IndexationFailedReason, err.Error()), "", err
|
||||
return NotReadyHelmRepository(repository, sourcev1.IndexationFailedReason, err.Error()), err
|
||||
}
|
||||
|
||||
index, err := yaml.Marshal(i)
|
||||
if err != nil {
|
||||
return NotReadyCondition(sourcev1.IndexationFailedReason, err.Error()), "", err
|
||||
return NotReadyHelmRepository(repository, sourcev1.IndexationFailedReason, err.Error()), err
|
||||
}
|
||||
|
||||
sum := r.Storage.Checksum(index)
|
||||
artifact := r.Storage.ArtifactFor(r.Kind, repository.ObjectMeta.GetObjectMeta(),
|
||||
fmt.Sprintf("index-%s.yaml", sum))
|
||||
fmt.Sprintf("index-%s.yaml", sum), sum)
|
||||
|
||||
// create artifact dir
|
||||
err = r.Storage.MkdirAll(artifact)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("unable to create repository index directory: %w", err)
|
||||
return NotReadyCondition(sourcev1.StorageOperationFailedReason, err.Error()), "", err
|
||||
return NotReadyHelmRepository(repository, sourcev1.StorageOperationFailedReason, err.Error()), err
|
||||
}
|
||||
|
||||
// acquire lock
|
||||
unlock, err := r.Storage.Lock(artifact)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("unable to acquire lock: %w", err)
|
||||
return NotReadyCondition(sourcev1.StorageOperationFailedReason, err.Error()), "", err
|
||||
return NotReadyHelmRepository(repository, sourcev1.StorageOperationFailedReason, err.Error()), err
|
||||
}
|
||||
defer unlock()
|
||||
|
||||
|
@ -186,19 +176,24 @@ func (r *HelmRepositoryReconciler) index(repository sourcev1.HelmRepository) (so
|
|||
err = r.Storage.WriteFile(artifact, index)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("unable to write repository index file: %w", err)
|
||||
return NotReadyCondition(sourcev1.StorageOperationFailedReason, err.Error()), "", err
|
||||
return NotReadyHelmRepository(repository, sourcev1.StorageOperationFailedReason, err.Error()), err
|
||||
}
|
||||
|
||||
message := fmt.Sprintf("Artifact is available at %s", artifact.Path)
|
||||
return ReadyCondition(sourcev1.IndexationSucceededReason, message), artifact.URL, nil
|
||||
// update index symlink
|
||||
indexUrl, err := r.Storage.Symlink(artifact, "index.yaml")
|
||||
if err != nil {
|
||||
err = fmt.Errorf("storage error %w", err)
|
||||
return NotReadyHelmRepository(repository, sourcev1.StorageOperationFailedReason, err.Error()), err
|
||||
}
|
||||
|
||||
message := fmt.Sprintf("Index is available at %s", artifact.Path)
|
||||
return ReadyHelmRepository(repository, artifact, indexUrl, sourcev1.IndexationSucceededReason, message), nil
|
||||
}
|
||||
|
||||
func (r *HelmRepositoryReconciler) shouldResetStatus(repository sourcev1.HelmRepository) (bool, sourcev1.HelmRepositoryStatus) {
|
||||
resetStatus := false
|
||||
if repository.Status.Artifact != "" {
|
||||
parts := strings.Split(repository.Status.Artifact, "/")
|
||||
artifact := r.Storage.ArtifactFor(r.Kind, repository.ObjectMeta.GetObjectMeta(), parts[len(parts)-1])
|
||||
if !r.Storage.ArtifactExist(artifact) {
|
||||
if repository.Status.Artifact != nil {
|
||||
if !r.Storage.ArtifactExist(*repository.Status.Artifact) {
|
||||
resetStatus = true
|
||||
}
|
||||
}
|
||||
|
@ -221,10 +216,8 @@ func (r *HelmRepositoryReconciler) shouldResetStatus(repository sourcev1.HelmRep
|
|||
}
|
||||
|
||||
func (r *HelmRepositoryReconciler) gc(repository sourcev1.HelmRepository) {
|
||||
if repository.Status.Artifact != "" {
|
||||
parts := strings.Split(repository.Status.Artifact, "/")
|
||||
artifact := r.Storage.ArtifactFor(r.Kind, repository.ObjectMeta.GetObjectMeta(), parts[len(parts)-1])
|
||||
if err := r.Storage.RemoveAllButCurrent(artifact); err != nil {
|
||||
if repository.Status.Artifact != nil {
|
||||
if err := r.Storage.RemoveAllButCurrent(*repository.Status.Artifact); err != nil {
|
||||
r.Log.Info("Artifacts GC failed", "error", err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import (
|
|||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1alpha1"
|
||||
"github.com/fluxcd/source-controller/internal/lockedfile"
|
||||
)
|
||||
|
||||
|
@ -44,15 +45,6 @@ type Storage struct {
|
|||
Timeout time.Duration `json:"timeout"`
|
||||
}
|
||||
|
||||
// Artifact represents the output of a source synchronisation
|
||||
type Artifact struct {
|
||||
// Path is the local file path of this artifact
|
||||
Path string `json:"path"`
|
||||
|
||||
// URL is the HTTP address of this artifact
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
// NewStorage creates the storage helper for a given path and hostname
|
||||
func NewStorage(basePath string, hostname string, timeout time.Duration) (*Storage, error) {
|
||||
if f, err := os.Stat(basePath); os.IsNotExist(err) || !f.IsDir() {
|
||||
|
@ -67,31 +59,33 @@ func NewStorage(basePath string, hostname string, timeout time.Duration) (*Stora
|
|||
}
|
||||
|
||||
// ArtifactFor returns an artifact for the given Kubernetes object
|
||||
func (s *Storage) ArtifactFor(kind string, metadata metav1.Object, fileName string) Artifact {
|
||||
func (s *Storage) ArtifactFor(kind string, metadata metav1.Object, fileName, revision string) sourcev1.Artifact {
|
||||
path := fmt.Sprintf("%s/%s-%s/%s", kind, metadata.GetName(), metadata.GetNamespace(), fileName)
|
||||
localPath := filepath.Join(s.BasePath, path)
|
||||
url := fmt.Sprintf("http://%s/%s", s.Hostname, path)
|
||||
|
||||
return Artifact{
|
||||
Path: localPath,
|
||||
URL: url,
|
||||
return sourcev1.Artifact{
|
||||
Path: localPath,
|
||||
URL: url,
|
||||
Revision: revision,
|
||||
LastUpdateTime: metav1.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
// MkdirAll calls os.MkdirAll for the given artifact base dir
|
||||
func (s *Storage) MkdirAll(artifact Artifact) error {
|
||||
func (s *Storage) MkdirAll(artifact sourcev1.Artifact) error {
|
||||
dir := filepath.Dir(artifact.Path)
|
||||
return os.MkdirAll(dir, 0777)
|
||||
}
|
||||
|
||||
// RemoveAll calls os.RemoveAll for the given artifact base dir
|
||||
func (s *Storage) RemoveAll(artifact Artifact) error {
|
||||
func (s *Storage) RemoveAll(artifact sourcev1.Artifact) error {
|
||||
dir := filepath.Dir(artifact.Path)
|
||||
return os.RemoveAll(dir)
|
||||
}
|
||||
|
||||
// RemoveAllButCurrent removes all files for the given artifact base dir excluding the current one
|
||||
func (s *Storage) RemoveAllButCurrent(artifact Artifact) error {
|
||||
func (s *Storage) RemoveAllButCurrent(artifact sourcev1.Artifact) error {
|
||||
dir := filepath.Dir(artifact.Path)
|
||||
errors := []string{}
|
||||
_ = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||
|
@ -110,7 +104,7 @@ func (s *Storage) RemoveAllButCurrent(artifact Artifact) error {
|
|||
}
|
||||
|
||||
// ArtifactExist returns a boolean indicating whether the artifact file exists in storage
|
||||
func (s *Storage) ArtifactExist(artifact Artifact) bool {
|
||||
func (s *Storage) ArtifactExist(artifact sourcev1.Artifact) bool {
|
||||
if _, err := os.Stat(artifact.Path); os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
|
@ -118,7 +112,7 @@ func (s *Storage) ArtifactExist(artifact Artifact) bool {
|
|||
}
|
||||
|
||||
// Archive creates a tar.gz to the artifact path from the given dir excluding the provided file extensions
|
||||
func (s *Storage) Archive(artifact Artifact, dir string, excludes string) error {
|
||||
func (s *Storage) Archive(artifact sourcev1.Artifact, dir string, excludes string) error {
|
||||
if excludes == "" {
|
||||
excludes = "jpg,jpeg,gif,png,wmv,flv,tar.gz,zip"
|
||||
}
|
||||
|
@ -138,7 +132,7 @@ func (s *Storage) Archive(artifact Artifact, dir string, excludes string) error
|
|||
}
|
||||
|
||||
// WriteFile writes the given bytes to the artifact path if the checksum differs
|
||||
func (s *Storage) WriteFile(artifact Artifact, data []byte) error {
|
||||
func (s *Storage) WriteFile(artifact sourcev1.Artifact, data []byte) error {
|
||||
sum := s.Checksum(data)
|
||||
if file, err := os.Stat(artifact.Path); !os.IsNotExist(err) && !file.IsDir() {
|
||||
if fb, err := ioutil.ReadFile(artifact.Path); err == nil && sum == s.Checksum(fb) {
|
||||
|
@ -150,24 +144,27 @@ func (s *Storage) WriteFile(artifact Artifact, data []byte) error {
|
|||
}
|
||||
|
||||
// Symlink creates or updates a symbolic link for the given artifact
|
||||
func (s *Storage) Symlink(artifact Artifact, linkName string) error {
|
||||
// and returns the URL for the symlink
|
||||
func (s *Storage) Symlink(artifact sourcev1.Artifact, linkName string) (string, error) {
|
||||
dir := filepath.Dir(artifact.Path)
|
||||
link := filepath.Join(dir, linkName)
|
||||
tmpLink := link + ".tmp"
|
||||
|
||||
if err := os.Remove(tmpLink); err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err := os.Symlink(artifact.Path, tmpLink); err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err := os.Rename(tmpLink, link); err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
|
||||
return nil
|
||||
parts := strings.Split(artifact.URL, "/")
|
||||
url := strings.Replace(artifact.URL, parts[len(parts)-1], linkName, 1)
|
||||
return url, nil
|
||||
}
|
||||
|
||||
// Checksum returns the SHA1 checksum for the given bytes as a string
|
||||
|
@ -176,7 +173,7 @@ func (s *Storage) Checksum(b []byte) string {
|
|||
}
|
||||
|
||||
// Lock creates a file lock for the given artifact
|
||||
func (s *Storage) Lock(artifact Artifact) (unlock func(), err error) {
|
||||
func (s *Storage) Lock(artifact sourcev1.Artifact) (unlock func(), err error) {
|
||||
lockFile := artifact.Path + ".lock"
|
||||
mutex := lockedfile.MutexAt(lockFile)
|
||||
return mutex.Lock()
|
||||
|
|
Loading…
Reference in New Issue