v2.0.0 ae3e752

rollback_handler.go RawHistoryFolder

1package events
2
3import (
4	"context"
5	"fmt"
6	"strings"
7
8	"github.com/rs/zerolog/log"
9	"gordon/internal/config"
10	"gordon/internal/container"
11	"gordon/pkg/manifest"
12)
13
14// VersionHandler handles version deployments based on manifest annotations
15type VersionHandler struct {
16	manager container.ManagerInterface
17	config  *config.Config
18}
19
20// NewVersionHandler creates a new version handler
21func NewVersionHandler(manager container.ManagerInterface, config *config.Config) *VersionHandler {
22	return &VersionHandler{
23		manager: manager,
24		config:  config,
25	}
26}
27
28// CanHandle returns true for ImagePushed events
29func (h *VersionHandler) CanHandle(eventType EventType) bool {
30	return eventType == ImagePushed
31}
32
33// Handle processes ImagePushed events and performs version deployments if necessary
34func (h *VersionHandler) Handle(event Event) error {
35	if event.Type != ImagePushed {
36		return nil
37	}
38
39	payload, ok := event.Data.(ImagePushedPayload)
40	if !ok {
41		return fmt.Errorf("invalid payload type for ImagePushed event")
42	}
43
44	// Skip if no annotations
45	if len(payload.Annotations) == 0 {
46		return nil
47	}
48
49	imageName := payload.Name
50	reference := payload.Reference
51	annotations := payload.Annotations
52
53	log.Debug().
54		Str("image", imageName).
55		Str("reference", reference).
56		Interface("annotations", annotations).
57		Msg("Processing annotations for version deployment")
58
59	// Check if this is a versioned deployment
60	if manifest.IsVersionedDeployment(annotations) {
61		return h.handleVersionedDeployment(imageName, reference, annotations)
62	}
63
64	return nil
65}
66
67// handleVersionedDeployment handles a versioned deployment
68func (h *VersionHandler) handleVersionedDeployment(imageName, reference string, annotations map[string]string) error {
69	version := manifest.GetDeploymentVersion(annotations)
70	if version == "" {
71		return nil
72	}
73
74	log.Info().
75		Str("image", imageName).
76		Str("reference", reference).
77		Str("version", version).
78		Msg("Deploying version via manifest annotation")
79
80	// Deploy the versioned image - treat this as a deployment to the specified version
81	return h.performVersionedDeployment(imageName, reference, version, annotations)
82}
83
84// performVersionedDeployment deploys a specific version based on manifest annotations
85func (h *VersionHandler) performVersionedDeployment(imageName, reference, version string, annotations map[string]string) error {
86	ctx := context.Background()
87	
88	// Find routes that use this image  
89	routes := h.findRoutesForImage(fmt.Sprintf("%s:%s", imageName, reference))
90	if len(routes) == 0 {
91		log.Debug().
92			Str("image", imageName).
93			Str("reference", reference).
94			Msg("No routes configured for versioned deployment image")
95		return nil
96	}
97
98	var errors []error
99	for _, route := range routes {
100		if err := h.deployVersion(ctx, route, imageName, version, annotations); err != nil {
101			log.Error().
102				Err(err).
103				Str("route", route).
104				Str("version", version).
105				Msg("Failed to deploy version to route")
106			errors = append(errors, fmt.Errorf("version deployment failed for route %s: %w", route, err))
107		} else {
108			log.Info().
109				Str("route", route).
110				Str("version", version).
111				Msg("Version deployment completed successfully")
112		}
113	}
114
115	if len(errors) > 0 {
116		return fmt.Errorf("version deployment completed with %d errors: %v", len(errors), errors)
117	}
118
119	return nil
120}
121
122
123// findRoutesForImage finds all routes that match the given image
124func (h *VersionHandler) findRoutesForImage(imageName string) []string {
125	var routes []string
126	
127	for route, configuredImage := range h.config.Routes {
128		if strings.EqualFold(configuredImage, imageName) {
129			routes = append(routes, route)
130		}
131	}
132	
133	return routes
134}
135
136// deployVersion deploys a specific version to a route
137func (h *VersionHandler) deployVersion(ctx context.Context, route, imageName, version string, annotations map[string]string) error {
138	// Construct the versioned image name
139	var versionedImage string
140	
141	if strings.Contains(version, ":") {
142		// Full image reference provided
143		versionedImage = version
144	} else {
145		// Just a version/tag provided, construct full image reference
146		versionedImage = fmt.Sprintf("%s:%s", imageName, version)
147	}
148
149	log.Info().
150		Str("route", route).
151		Str("current_image", h.config.Routes[route]).
152		Str("versioned_image", versionedImage).
153		Msg("Performing version deployment")
154
155	// Update the route configuration to the versioned image
156	if err := h.config.UpdateRoute(route, versionedImage); err != nil {
157		return fmt.Errorf("failed to update route configuration for version deployment: %w", err)
158	}
159
160	// Deploy the versioned image
161	routeConfig := config.Route{
162		Domain: route,
163		Image:  versionedImage,
164		HTTPS:  true,
165	}
166
167	_, err := h.manager.DeployContainer(ctx, routeConfig)
168	if err != nil {
169		// Attempt to restore original configuration
170		if originalImage := h.config.Routes[route]; originalImage != versionedImage {
171			log.Warn().
172				Str("route", route).
173				Str("original_image", originalImage).
174				Msg("Version deployment failed, attempting to restore original configuration")
175			_ = h.config.UpdateRoute(route, originalImage)
176		}
177		return fmt.Errorf("failed to deploy versioned container: %w", err)
178	}
179
180	// Log successful deployment
181	log.Info().
182		Str("route", route).
183		Str("versioned_image", versionedImage).
184		Interface("version_annotations", annotations).
185		Msg("Version deployment completed successfully")
186
187	return nil
188}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 7

Tags 106

↑↓ moveenter openesc close