Skip to content

Commit 191a238

Browse files
committed
add remoteBuild for flex consumption apps
1 parent c51b2d9 commit 191a238

File tree

7 files changed

+1984
-1871
lines changed

7 files changed

+1984
-1871
lines changed

.vscode/cspell.misc.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,4 @@ overrides:
3535
- myimage
3636
- azureai
3737
- entra
38+
- flexconsumption

cli/azd/pkg/azapi/azure_client_functions_test.go

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ func Test_GetFunctionAppProperties(t *testing.T) {
8181
})
8282
}
8383

84-
func Test_DeployFunctionAppUsingZipFile(t *testing.T) {
84+
func Test_DeployFunctionAppUsingZipFileRegular(t *testing.T) {
8585
t.Run("Success", func(t *testing.T) {
8686
ran := false
8787
mockContext := mocks.NewMockContext(context.Background())
@@ -93,13 +93,12 @@ func Test_DeployFunctionAppUsingZipFile(t *testing.T) {
9393

9494
zipFile := bytes.NewReader([]byte{})
9595

96-
res, err := azCli.DeployFunctionAppUsingZipFile(
96+
res, err := azCli.DeployFunctionAppUsingZipFileRegular(
9797
*mockContext.Context,
9898
"SUBSCRIPTION_ID",
9999
"RESOURCE_GROUP_ID",
100100
"FUNC_APP_NAME",
101101
zipFile,
102-
false,
103102
)
104103

105104
require.NoError(t, err)
@@ -117,13 +116,12 @@ func Test_DeployFunctionAppUsingZipFile(t *testing.T) {
117116

118117
zipFile := bytes.NewReader([]byte{})
119118

120-
res, err := azCli.DeployFunctionAppUsingZipFile(
119+
res, err := azCli.DeployFunctionAppUsingZipFileRegular(
121120
*mockContext.Context,
122121
"SUBSCRIPTION_ID",
123122
"RESOURCE_GROUP_ID",
124123
"FUNC_APP_NAME",
125124
zipFile,
126-
false,
127125
)
128126

129127
require.Nil(t, res)

cli/azd/pkg/azapi/function_app.go

Lines changed: 56 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
"context"
88
"fmt"
99
"io"
10-
"strings"
1110

1211
"github.com/Azure/azure-sdk-for-go/sdk/azcore/arm"
1312
"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
@@ -35,7 +34,43 @@ func (cli *AzureClient) GetFunctionAppProperties(
3534
}, nil
3635
}
3736

38-
func (cli *AzureClient) DeployFunctionAppUsingZipFile(
37+
// GetFunctionAppPlan retrieves the app service plan for a function app
38+
func (cli *AzureClient) GetFunctionAppPlan(
39+
ctx context.Context,
40+
subscriptionId string,
41+
resourceGroup string,
42+
appName string,
43+
) (*armappservice.Plan, error) {
44+
app, err := cli.appService(ctx, subscriptionId, resourceGroup, appName)
45+
if err != nil {
46+
return nil, err
47+
}
48+
49+
planId, err := arm.ParseResourceID(*app.Properties.ServerFarmID)
50+
if err != nil {
51+
return nil, err
52+
}
53+
54+
plansCred, err := cli.credentialProvider.CredentialForSubscription(ctx, planId.SubscriptionID)
55+
if err != nil {
56+
return nil, err
57+
}
58+
59+
plansClient, err := armappservice.NewPlansClient(planId.SubscriptionID, plansCred, cli.armClientOptions)
60+
if err != nil {
61+
return nil, err
62+
}
63+
64+
planResp, err := plansClient.Get(ctx, planId.ResourceGroupName, planId.Name, nil)
65+
if err != nil {
66+
return nil, err
67+
}
68+
69+
return &planResp.Plan, nil
70+
}
71+
72+
// DeployFunctionAppUsingZipFileFlexConsumption deploys to a Flex Consumption function app
73+
func (cli *AzureClient) DeployFunctionAppUsingZipFileFlexConsumption(
3974
ctx context.Context,
4075
subscriptionId string,
4176
resourceGroup string,
@@ -53,42 +88,39 @@ func (cli *AzureClient) DeployFunctionAppUsingZipFile(
5388
return nil, err
5489
}
5590

56-
planId, err := arm.ParseResourceID(*app.Properties.ServerFarmID)
91+
cred, err := cli.credentialProvider.CredentialForSubscription(ctx, subscriptionId)
5792
if err != nil {
5893
return nil, err
5994
}
6095

61-
plansCred, err := cli.credentialProvider.CredentialForSubscription(ctx, planId.SubscriptionID)
96+
client, err := azsdk.NewFuncAppHostClient(hostName, cred, cli.armClientOptions)
6297
if err != nil {
63-
return nil, err
98+
return nil, fmt.Errorf("creating func app host client: %w", err)
6499
}
65100

66-
plansClient, err := armappservice.NewPlansClient(planId.SubscriptionID, plansCred, cli.armClientOptions)
101+
response, err := client.Publish(ctx, deployZipFile, &azsdk.PublishOptions{RemoteBuild: remoteBuild})
67102
if err != nil {
68-
return nil, err
103+
return nil, fmt.Errorf("publishing zip file: %w", err)
69104
}
105+
return to.Ptr(response.StatusText), nil
106+
}
70107

71-
plan, err := plansClient.Get(ctx, planId.ResourceGroupName, planId.Name, nil)
108+
// DeployFunctionAppUsingZipFileRegular deploys to a regular (non-Flex Consumption) function app
109+
func (cli *AzureClient) DeployFunctionAppUsingZipFileRegular(
110+
ctx context.Context,
111+
subscriptionId string,
112+
resourceGroup string,
113+
appName string,
114+
deployZipFile io.ReadSeeker,
115+
) (*string, error) {
116+
app, err := cli.appService(ctx, subscriptionId, resourceGroup, appName)
72117
if err != nil {
73118
return nil, err
74119
}
75120

76-
if strings.ToLower(*plan.SKU.Tier) == "flexconsumption" {
77-
cred, err := cli.credentialProvider.CredentialForSubscription(ctx, subscriptionId)
78-
if err != nil {
79-
return nil, err
80-
}
81-
82-
client, err := azsdk.NewFuncAppHostClient(hostName, cred, cli.armClientOptions)
83-
if err != nil {
84-
return nil, fmt.Errorf("creating func app host client: %w", err)
85-
}
86-
87-
response, err := client.Publish(ctx, deployZipFile, &azsdk.PublishOptions{RemoteBuild: remoteBuild})
88-
if err != nil {
89-
return nil, fmt.Errorf("publishing zip file: %w", err)
90-
}
91-
return to.Ptr(response.StatusText), nil
121+
hostName, err := appServiceRepositoryHost(app, appName)
122+
if err != nil {
123+
return nil, err
92124
}
93125

94126
client, err := cli.createZipDeployClient(ctx, subscriptionId, hostName)

cli/azd/pkg/project/service_config.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ type ServiceConfig struct {
5959
// Condition for deploying the service. When evaluated, the service is only deployed if the value
6060
// is a truthy boolean (1, true, TRUE, True, yes). If not defined, the service is enabled by default.
6161
Condition osutil.ExpandableString `yaml:"condition,omitempty"`
62+
// Whether to build the service remotely. Only applicable to function app services.
63+
// When set to nil (unset), the default behavior based on language is used.
64+
RemoteBuild *bool `yaml:"remoteBuild,omitempty"`
6265

6366
// AdditionalProperties captures any unknown YAML fields for extension support
6467
AdditionalProperties map[string]interface{} `yaml:",inline"`

cli/azd/pkg/project/service_target_functionapp.go

Lines changed: 55 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,28 +10,33 @@ import (
1010
"path/filepath"
1111
"strings"
1212

13+
"github.com/azure/azure-dev/cli/azd/internal"
1314
"github.com/azure/azure-dev/cli/azd/internal/mapper"
1415
"github.com/azure/azure-dev/cli/azd/pkg/async"
1516
"github.com/azure/azure-dev/cli/azd/pkg/azapi"
1617
"github.com/azure/azure-dev/cli/azd/pkg/environment"
18+
"github.com/azure/azure-dev/cli/azd/pkg/input"
1719
"github.com/azure/azure-dev/cli/azd/pkg/tools"
1820
)
1921

2022
// functionAppTarget specifies an Azure Function to deploy to.
2123
// Implements `project.ServiceTarget`
2224
type functionAppTarget struct {
23-
env *environment.Environment
24-
cli *azapi.AzureClient
25+
env *environment.Environment
26+
cli *azapi.AzureClient
27+
console input.Console
2528
}
2629

2730
// NewFunctionAppTarget creates a new instance of the Function App target
2831
func NewFunctionAppTarget(
2932
env *environment.Environment,
3033
azCli *azapi.AzureClient,
34+
console input.Console,
3135
) ServiceTarget {
3236
return &functionAppTarget{
33-
env: env,
34-
cli: azCli,
37+
env: env,
38+
cli: azCli,
39+
console: console,
3540
}
3641
}
3742

@@ -124,19 +129,57 @@ func (f *functionAppTarget) Deploy(
124129

125130
defer zipFile.Close()
126131

127-
progress.SetProgress(NewServiceProgress("Uploading deployment package"))
128-
remoteBuild := serviceConfig.Language == ServiceLanguageJavaScript ||
129-
serviceConfig.Language == ServiceLanguageTypeScript ||
130-
serviceConfig.Language == ServiceLanguagePython
131-
132-
_, err = f.cli.DeployFunctionAppUsingZipFile(
132+
// Get the function app's plan
133+
plan, err := f.cli.GetFunctionAppPlan(
133134
ctx,
134135
targetResource.SubscriptionId(),
135136
targetResource.ResourceGroupName(),
136137
targetResource.ResourceName(),
137-
zipFile,
138-
remoteBuild,
139138
)
139+
if err != nil {
140+
return nil, fmt.Errorf("determining function app plan type: %w", err)
141+
}
142+
143+
isFlexConsumption := strings.EqualFold(*plan.SKU.Tier, "flexconsumption")
144+
145+
if serviceConfig.RemoteBuild != nil && !isFlexConsumption {
146+
return nil, &internal.ErrorWithSuggestion{
147+
Err: fmt.Errorf("'remoteBuild' is only supported for Flex Consumption plan function apps"),
148+
Suggestion: "For other plan types, set these environment variables on the function app:\n" +
149+
" ENABLE_ORYX_BUILD=true\n" +
150+
" SCM_DO_BUILD_DURING_DEPLOYMENT=true",
151+
}
152+
}
153+
154+
progress.SetProgress(NewServiceProgress("Uploading deployment package"))
155+
var remoteBuild bool
156+
if serviceConfig.RemoteBuild != nil {
157+
remoteBuild = *serviceConfig.RemoteBuild
158+
} else {
159+
remoteBuild = serviceConfig.Language == ServiceLanguageJavaScript ||
160+
serviceConfig.Language == ServiceLanguageTypeScript ||
161+
serviceConfig.Language == ServiceLanguagePython
162+
}
163+
164+
// Deploy to appropriate plan type
165+
if isFlexConsumption {
166+
_, err = f.cli.DeployFunctionAppUsingZipFileFlexConsumption(
167+
ctx,
168+
targetResource.SubscriptionId(),
169+
targetResource.ResourceGroupName(),
170+
targetResource.ResourceName(),
171+
zipFile,
172+
remoteBuild,
173+
)
174+
} else {
175+
_, err = f.cli.DeployFunctionAppUsingZipFileRegular(
176+
ctx,
177+
targetResource.SubscriptionId(),
178+
targetResource.ResourceGroupName(),
179+
targetResource.ResourceName(),
180+
zipFile,
181+
)
182+
}
140183
if err != nil {
141184
return nil, err
142185
}

0 commit comments

Comments
 (0)