mirror of
https://github.com/hanzoai/deploy.git
synced 2026-10-07 08:44:50 +00:00
go.mod still declared `module github.com/hanzoai/deploy` after the repo was renamed. A Go module path is not cosmetic — it is the resolvable location. So `go get github.com/hanzoai/cd` could not work, and anything importing this module had to spell a name that no longer exists at that address. The build log of v3.6.0 is where it surfaced: the compiler was still emitting github.com/hanzoai/deploy/cmd/cd/commands. 3541 import paths across 690 .go, 19 .proto, 4 .yaml, 3 .sh, 2 .mod, Procfile and Makefile. Plus the 62 ghcr.io/hanzoai/deploy image references, moved to ghcr.io/hanzoai/cd to match hanzo.yml's build target — a distinct string from the module path, replaced separately so neither could clobber the other. go mod tidy clean, CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build ./... clean. Co-authored-by: Hanzo Dev <[email protected]>
149 lines
6.0 KiB
Go
149 lines
6.0 KiB
Go
package controller
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"maps"
|
|
|
|
"github.com/hanzoai/cd/controller/hydrator/types"
|
|
appv1 "github.com/hanzoai/cd/pkg/apis/application/v1alpha1"
|
|
"github.com/hanzoai/cd/reposerver/apiclient"
|
|
argoutil "github.com/hanzoai/cd/util/cd"
|
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
|
)
|
|
|
|
/**
|
|
This file implements the hydrator.Dependencies interface for the ApplicationController.
|
|
|
|
Hydration logic does not belong in this file. The methods here should be "bookkeeping" methods that keep hydration work
|
|
in the hydrator and app controller work in the app controller. The only purpose of this file is to provide the hydrator
|
|
safe, minimal access to certain app controller functionality to avoid duplicate code.
|
|
*/
|
|
|
|
func (ctrl *ApplicationController) GetProcessableAppProj(app *appv1.Application) (*appv1.AppProject, error) {
|
|
return ctrl.getAppProj(app)
|
|
}
|
|
|
|
// GetProcessableApps returns a list of applications that are processable by the controller.
|
|
func (ctrl *ApplicationController) GetProcessableApps() (*appv1.ApplicationList, error) {
|
|
// getAppList already filters out applications that are not processable by the controller.
|
|
return ctrl.getAppList(metav1.ListOptions{})
|
|
}
|
|
|
|
func (ctrl *ApplicationController) EvaluateAppRevisionsChanges(ctx context.Context, app *appv1.Application, source appv1.ApplicationSource, revision string, project *appv1.AppProject, noRevisionCache bool) (bool, string, error) {
|
|
sources := []appv1.ApplicationSource{source}
|
|
revisions := []string{revision}
|
|
|
|
hasChanges, resolvedRevisions, err := ctrl.appStateManager.EvaluateAppRevisionsChanges(ctx, app, sources, revisions, project, false, noRevisionCache)
|
|
if err != nil {
|
|
return false, "", err
|
|
}
|
|
if len(resolvedRevisions) != 1 {
|
|
return false, "", fmt.Errorf("expected exactly one resolved revision for single source, got %d", len(resolvedRevisions))
|
|
}
|
|
if resolvedRevisions[0] == "" {
|
|
return false, "", errors.New("resolved revision is empty for dry source")
|
|
}
|
|
return hasChanges, resolvedRevisions[0], nil
|
|
}
|
|
|
|
func (ctrl *ApplicationController) GetRepoObjs(ctx context.Context, app *appv1.Application, drySource appv1.ApplicationSource, revision string, project *appv1.AppProject) ([]*unstructured.Unstructured, *apiclient.ManifestResponse, error) {
|
|
drySources := []appv1.ApplicationSource{drySource}
|
|
dryRevisions := []string{revision}
|
|
|
|
appLabelKey, err := ctrl.settingsMgr.GetAppInstanceLabelKey()
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("failed to get app instance label key: %w", err)
|
|
}
|
|
|
|
// FIXME: use cache and revision cache
|
|
objs, resp, _, err := ctrl.appStateManager.GetRepoObjs(ctx, app, drySources, appLabelKey, dryRevisions, true, true, project.EffectiveSourceIntegrity(), project, false)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("failed to get repo objects: %w", err)
|
|
}
|
|
|
|
trackingMethod, err := ctrl.settingsMgr.GetTrackingMethod()
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("failed to get tracking method: %w", err)
|
|
}
|
|
for _, obj := range objs {
|
|
if err := argoutil.NewResourceTracking().RemoveAppInstance(obj, trackingMethod); err != nil {
|
|
return nil, nil, fmt.Errorf("failed to remove the app instance value: %w", err)
|
|
}
|
|
}
|
|
|
|
if len(resp) != 1 {
|
|
return nil, nil, fmt.Errorf("expected one manifest response, got %d", len(resp))
|
|
}
|
|
|
|
return objs, resp[0], nil
|
|
}
|
|
|
|
func (ctrl *ApplicationController) GetWriteCredentials(ctx context.Context, repoURL string, project string) (*appv1.Repository, error) {
|
|
return ctrl.db.GetWriteRepository(ctx, repoURL, project)
|
|
}
|
|
|
|
func (ctrl *ApplicationController) RequestAppRefresh(appName string, appNamespace string) error {
|
|
// We request a refresh by setting the annotation instead of by adding it to the refresh queue, because there is no
|
|
// guarantee that the hydrator is running on the same controller shard as is processing the application.
|
|
|
|
// This function is called for each app after a hydrate operation is completed so that the app controller can pick
|
|
// up the newly-hydrated changes. So we pass nil hydrateType to avoid a hydrate loop.
|
|
_, err := argoutil.RefreshApp(ctrl.applicationClientset.ArgoprojV1alpha1().Applications(appNamespace), appName, appv1.RefreshTypeNormal, nil)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to request app refresh: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (ctrl *ApplicationController) PersistHydrationStatus(orig *appv1.Application, newStatus *appv1.SourceHydratorStatus) {
|
|
newAnnotations := make(map[string]string)
|
|
maps.Copy(newAnnotations, orig.GetAnnotations())
|
|
delete(newAnnotations, appv1.AnnotationKeyHydrate)
|
|
status := orig.Status.DeepCopy()
|
|
status.SourceHydrator = *newStatus
|
|
// PersistHydrationStatus has no request context; the status write roots its own trace.
|
|
ctrl.persistAppStatus(context.Background(), orig, status, newAnnotations)
|
|
}
|
|
|
|
func (ctrl *ApplicationController) AddHydrationQueueItem(key types.HydrationQueueKey) {
|
|
ctrl.hydrationQueue.AddRateLimited(key)
|
|
}
|
|
|
|
func (ctrl *ApplicationController) GetHydratorCommitMessageTemplate() (string, error) {
|
|
sourceHydratorCommitMessageKey, err := ctrl.settingsMgr.GetSourceHydratorCommitMessageTemplate()
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to get sourceHydrator commit message template key: %w", err)
|
|
}
|
|
|
|
return sourceHydratorCommitMessageKey, nil
|
|
}
|
|
|
|
func (ctrl *ApplicationController) GetHydratorReadmeMessageTemplate() (string, error) {
|
|
readmeTemplate, err := ctrl.settingsMgr.GetHydratorReadmeTemplate()
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to get sourceHydrator README message template: %w", err)
|
|
}
|
|
|
|
return readmeTemplate, nil
|
|
}
|
|
|
|
func (ctrl *ApplicationController) GetCommitAuthorName() (string, error) {
|
|
authorName, err := ctrl.settingsMgr.GetCommitAuthorName()
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to get commit author name: %w", err)
|
|
}
|
|
return authorName, nil
|
|
}
|
|
|
|
func (ctrl *ApplicationController) GetCommitAuthorEmail() (string, error) {
|
|
authorEmail, err := ctrl.settingsMgr.GetCommitAuthorEmail()
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to get commit author email: %w", err)
|
|
}
|
|
return authorEmail, nil
|
|
}
|