/
environment_service_load.go
133 lines (111 loc) · 4.62 KB
/
environment_service_load.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package vsrpc
import (
"context"
"fmt"
"strings"
"github.com/azure/azure-dev/cli/azd/pkg/environment"
"github.com/azure/azure-dev/cli/azd/pkg/environment/azdcontext"
"github.com/azure/azure-dev/cli/azd/pkg/project"
"github.com/azure/azure-dev/cli/azd/pkg/tools/dotnet"
)
// OpenEnvironmentAsync is the server implementation of:
// ValueTask<Environment> OpenEnvironmentAsync(RequestContext, string, IObserver<ProgressMessage>, CancellationToken);
//
// OpenEnvironmentAsync loads the specified environment, without connecting to Azure or fetching a manifest (unless it is
// already cached) and is faster than `LoadEnvironmentAsync` in cases where we have not cached the manifest. This means
// the Services array of the returned environment may be empty.
func (s *environmentService) OpenEnvironmentAsync(
ctx context.Context, rc RequestContext, name string, observer IObserver[ProgressMessage],
) (*Environment, error) {
session, err := s.server.validateSession(ctx, rc.Session)
if err != nil {
return nil, err
}
container, err := session.newContainer()
if err != nil {
return nil, err
}
return s.loadEnvironmentAsync(ctx, container, name, false)
}
// LoadEnvironmentAsync is the server implementation of:
// ValueTask<Environment> LoadEnvironmentAsync(RequestContext, string, IObserver<ProgressMessage>, CancellationToken);
//
// LoadEnvironmentAsync loads the specified environment, without connecting to Azure. Because of this, certain properties of
// the environment (like service endpoints) may not be available. Use `RefreshEnvironmentAsync` to load the environment and
// fetch information from Azure.
func (s *environmentService) LoadEnvironmentAsync(
ctx context.Context, rc RequestContext, name string, observer IObserver[ProgressMessage],
) (*Environment, error) {
session, err := s.server.validateSession(ctx, rc.Session)
if err != nil {
return nil, err
}
container, err := session.newContainer()
if err != nil {
return nil, err
}
return s.loadEnvironmentAsync(ctx, container, name, true)
}
func (s *environmentService) loadEnvironmentAsync(
ctx context.Context, container *container, name string, mustLoadServices bool,
) (*Environment, error) {
var c struct {
azdCtx *azdcontext.AzdContext `container:"type"`
envManager environment.Manager `container:"type"`
projectConfig *project.ProjectConfig `container:"type"`
dotnetCli dotnet.DotNetCli `container:"type"`
dotnetImporter *project.DotNetImporter `container:"type"`
}
if err := container.Fill(&c); err != nil {
return nil, err
}
e, err := c.envManager.Get(ctx, name)
if err != nil {
return nil, fmt.Errorf("getting environment: %w", err)
}
currentEnv, err := c.azdCtx.GetDefaultEnvironmentName()
if err != nil {
return nil, fmt.Errorf("getting default environment: %w", err)
}
ret := &Environment{
Name: name,
Properties: map[string]string{
"Subscription": e.GetSubscriptionId(),
"Location": e.GetLocation(),
},
IsCurrent: name == currentEnv,
Values: e.Dotenv(),
}
// NOTE(ellismg): The IaC for Aspire Apps exposes these properties - we use them instead of trying to discover the
// deployed resources (perhaps by considering the resources in the resource group associated with the environment or
// by looking at the deployment). This was the quickest path to get the information that VS needed for the spike,
// but we might want to revisit this strategy. A nice thing about this strategy is it means we can return the data
// promptly, which is nice for VS.
if v := e.Getenv("AZURE_CONTAINER_APPS_ENVIRONMENT_ID"); v != "" {
parts := strings.Split(v, "/")
ret.Properties["ContainerAppsEnvironment"] = parts[len(parts)-1]
}
if v := e.Getenv("AZURE_CONTAINER_REGISTRY_ENDPOINT"); v != "" {
ret.Properties["ContainerRegistry"] = strings.TrimSuffix(v, ".azurecr.io")
}
if v := e.Getenv("AZURE_LOG_ANALYTICS_WORKSPACE_NAME"); v != "" {
ret.Properties["LogAnalyticsWorkspace"] = v
}
// If we would have to discover the app host or load the manifest from disk and the caller did not request it
// skip this somewhat expensive operation, at the expense of not building out the services array.
if !mustLoadServices {
return ret, nil
}
appHost, err := appHostForProject(ctx, c.projectConfig, c.dotnetCli)
if err != nil {
return nil, fmt.Errorf("failed to find Aspire app host: %w", err)
}
manifest, err := c.dotnetImporter.ReadManifest(ctx, appHost)
if err != nil {
return nil, fmt.Errorf("reading app host manifest: %w", err)
}
ret.Services = servicesFromManifest(manifest)
return ret, nil
}