|
8 | 8 | "os/exec" |
9 | 9 | "path/filepath" |
10 | 10 | "runtime" |
| 11 | + "sort" |
11 | 12 | "strings" |
12 | 13 | "sync" |
13 | 14 |
|
@@ -193,21 +194,88 @@ func (m *Manager) List(ctx context.Context) ([]*Environment, error) { |
193 | 194 | m.mu.RLock() |
194 | 195 | defer m.mu.RUnlock() |
195 | 196 |
|
196 | | - var allEnvs []*Environment |
| 197 | + state, err := m.loadState() |
| 198 | + if err != nil { |
| 199 | + state = make(map[string]*Environment) |
| 200 | + } |
| 201 | + |
| 202 | + runtimeByMode := make(map[Mode][]*Environment) |
| 203 | + runtimeIDs := make(map[Mode]map[string]struct{}) |
| 204 | + offlineModes := make(map[Mode]struct{}) |
| 205 | + |
197 | 206 | for _, backend := range m.backends { |
| 207 | + mode := backend.Mode() |
198 | 208 | if !backend.IsAvailable(ctx) { |
| 209 | + offlineModes[mode] = struct{}{} |
199 | 210 | continue |
200 | 211 | } |
201 | 212 | envs, err := backend.List(ctx) |
202 | 213 | if err != nil { |
203 | | - continue // Log but continue with other backends |
| 214 | + offlineModes[mode] = struct{}{} |
| 215 | + continue |
| 216 | + } |
| 217 | + runtimeByMode[mode] = envs |
| 218 | + ids := make(map[string]struct{}, len(envs)) |
| 219 | + for _, env := range envs { |
| 220 | + ids[env.ID] = struct{}{} |
| 221 | + } |
| 222 | + runtimeIDs[mode] = ids |
| 223 | + } |
| 224 | + |
| 225 | + var allEnvs []*Environment |
| 226 | + includedIDs := make(map[string]struct{}) |
| 227 | + for _, envs := range runtimeByMode { |
| 228 | + for _, env := range envs { |
| 229 | + allEnvs = append(allEnvs, env) |
| 230 | + includedIDs[env.ID] = struct{}{} |
| 231 | + } |
| 232 | + } |
| 233 | + |
| 234 | + stateEnvs := make([]*Environment, 0, len(state)) |
| 235 | + for _, env := range state { |
| 236 | + stateEnvs = append(stateEnvs, env) |
| 237 | + } |
| 238 | + sort.Slice(stateEnvs, func(i, j int) bool { |
| 239 | + if stateEnvs[i].Name == stateEnvs[j].Name { |
| 240 | + return stateEnvs[i].ID < stateEnvs[j].ID |
| 241 | + } |
| 242 | + return stateEnvs[i].Name < stateEnvs[j].Name |
| 243 | + }) |
| 244 | + |
| 245 | + for _, env := range stateEnvs { |
| 246 | + if _, ok := includedIDs[env.ID]; ok { |
| 247 | + continue |
| 248 | + } |
| 249 | + |
| 250 | + envCopy := cloneEnvironment(env) |
| 251 | + if _, offline := offlineModes[env.Mode]; offline { |
| 252 | + envCopy.Status = StatusUnknown |
| 253 | + } else if _, ok := runtimeIDs[env.Mode]; ok { |
| 254 | + envCopy.Status = StatusDeleted |
| 255 | + } else { |
| 256 | + envCopy.Status = StatusUnknown |
204 | 257 | } |
205 | | - allEnvs = append(allEnvs, envs...) |
| 258 | + allEnvs = append(allEnvs, envCopy) |
206 | 259 | } |
207 | 260 |
|
208 | 261 | return allEnvs, nil |
209 | 262 | } |
210 | 263 |
|
| 264 | +func cloneEnvironment(env *Environment) *Environment { |
| 265 | + if env == nil { |
| 266 | + return nil |
| 267 | + } |
| 268 | + copyEnv := *env |
| 269 | + if env.Labels != nil { |
| 270 | + labels := make(map[string]string, len(env.Labels)) |
| 271 | + for k, v := range env.Labels { |
| 272 | + labels[k] = v |
| 273 | + } |
| 274 | + copyEnv.Labels = labels |
| 275 | + } |
| 276 | + return ©Env |
| 277 | +} |
| 278 | + |
211 | 279 | // Stop stops an environment |
212 | 280 | func (m *Manager) Stop(ctx context.Context, idOrName string) error { |
213 | 281 | env, err := m.Get(ctx, idOrName) |
|
0 commit comments