| 227 | } |
| 228 | |
| 229 | func (n *Namespace) Restore(ctx context.Context, r io.Reader) (*Application, error) { |
| 230 | appSettings, volSettings, volumeData, err := n.parseBackup(r) |
| 231 | if err != nil { |
| 232 | return nil, fmt.Errorf("parsing backup: %w", err) |
| 233 | } |
| 234 | |
| 235 | if n.HostInUse(appSettings.Host) { |
| 236 | return nil, ErrHostnameInUse |
| 237 | } |
| 238 | |
| 239 | name, err := n.UniqueName(NameFromImageRef(appSettings.Image)) |
| 240 | if err != nil { |
| 241 | return nil, fmt.Errorf("generating app name: %w", err) |
| 242 | } |
| 243 | appSettings.Name = name |
| 244 | |
| 245 | app := NewApplication(n, appSettings) |
| 246 | if err := app.Restore(ctx, volSettings, volumeData); err != nil { |
| 247 | if cleanupErr := app.Destroy(context.Background(), true); cleanupErr != nil { |
| 248 | slog.Error("Failed to clean up after restore failure", "app", appSettings.Name, "error", cleanupErr) |
| 249 | } |
| 250 | return nil, err |
| 251 | } |
| 252 | |
| 253 | if err := n.Refresh(ctx); err != nil { |
| 254 | slog.Error("Failed to refresh namespace after restore", "app", appSettings.Name, "error", err) |
| 255 | } |
| 256 | |
| 257 | if restored := n.Application(appSettings.Name); restored != nil { |
| 258 | return restored, nil |
| 259 | } |
| 260 | return app, nil |
| 261 | } |
| 262 | |
| 263 | // containerAppName extracts the application name from a container name |
| 264 | // matching the pattern {namespace}-app-{appName}-{id}. Returns "" if the |