99 "context"
1010 "encoding/json"
1111 "fmt"
12- "time"
1312
1413 r "github.com/Azure/azure-sdk-for-go/profiles/latest/resources/mgmt/resources"
1514 n "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-09-01/network"
@@ -22,17 +21,12 @@ import (
2221 "github.com/Azure/application-gateway-kubernetes-ingress/pkg/version"
2322)
2423
25- const (
26- retryPause = 10 * time .Second
27- retryCount = 3
28- extendedRetryCount = 60
29- )
30-
3124// AzClient is an interface for client to Azure
3225type AzClient interface {
3326 SetAuthorizer (authorizer autorest.Authorizer )
3427
3528 ApplyRouteTable (string , string ) error
29+ WaitForGetAccessOnGateway () error
3630 GetGateway () (n.ApplicationGateway , error )
3731 UpdateGateway (* n.ApplicationGateway ) error
3832 DeployGatewayWithVnet (ResourceGroup , ResourceName , ResourceName , string ) error
@@ -49,6 +43,7 @@ type azClient struct {
4943 routeTablesClient n.RouteTablesClient
5044 groupsClient r.GroupsClient
5145 deploymentsClient r.DeploymentsClient
46+ clientID string
5247
5348 subscriptionID SubscriptionID
5449 resourceGroupName ResourceGroup
@@ -59,7 +54,7 @@ type azClient struct {
5954}
6055
6156// NewAzClient returns an Azure Client
62- func NewAzClient (subscriptionID SubscriptionID , resourceGroupName ResourceGroup , appGwName ResourceName ) AzClient {
57+ func NewAzClient (subscriptionID SubscriptionID , resourceGroupName ResourceGroup , appGwName ResourceName , clientID string ) AzClient {
6358 settings , err := auth .GetSettingsFromEnvironment ()
6459 if err != nil {
6560 return nil
@@ -74,6 +69,7 @@ func NewAzClient(subscriptionID SubscriptionID, resourceGroupName ResourceGroup,
7469 routeTablesClient : n .NewRouteTablesClientWithBaseURI (settings .Environment .ResourceManagerEndpoint , string (subscriptionID )),
7570 groupsClient : r .NewGroupsClientWithBaseURI (settings .Environment .ResourceManagerEndpoint , string (subscriptionID )),
7671 deploymentsClient : r .NewDeploymentsClientWithBaseURI (settings .Environment .ResourceManagerEndpoint , string (subscriptionID )),
72+ clientID : clientID ,
7773
7874 subscriptionID : subscriptionID ,
7975 resourceGroupName : resourceGroupName ,
@@ -118,50 +114,80 @@ func (az *azClient) SetAuthorizer(authorizer autorest.Authorizer) {
118114 az .deploymentsClient .Authorizer = authorizer
119115}
120116
121- func (az * azClient ) GetGateway () (response n.ApplicationGateway , err error ) {
122- err = utils .Retry (retryCount , retryPause ,
117+ func (az * azClient ) WaitForGetAccessOnGateway () (err error ) {
118+ glog .V (5 ).Info ("Getting Application Gateway configuration." )
119+ err = utils .Retry (- 1 , retryPause ,
123120 func () (utils.Retriable , error ) {
124- response , err = az .appGatewaysClient .Get (az .ctx , string (az .resourceGroupName ), string (az .appGwName ))
121+ response , err : = az .appGatewaysClient .Get (az .ctx , string (az .resourceGroupName ), string (az .appGwName ))
125122 if err == nil {
126- return utils .Retriable (false ), nil
123+ return utils .Retriable (true ), nil
127124 }
128125
129- // Reasons for 403 errors
130- if response .Response .Response != nil && response .Response .StatusCode == 403 {
131- glog .Error ("Following might be potential reasons:\n " +
132- " AKS Service Principal requires 'Managed Identity Operator' access on Controller Identity\n " +
133- " 'identityResourceID' and/or 'identityClientID' are incorrect in the Helm config\n " +
134- " AGIC Identity requires 'Contributor' access on Application Gateway and 'Reader' access on Application Gateway's Resource Group\n " +
135- " Please check the AAD Pod Identity mni and nmi pod logs to find potential issues." )
136- }
126+ e := controllererrors .NewErrorWithInnerErrorf (
127+ controllererrors .ErrorGetApplicationGatewayError ,
128+ err ,
129+ "Failed fetching configuration for Application Gateway. Will retry in %v." , retryPause ,
130+ )
137131
138- if response .Response .Response != nil && response . Response . StatusCode == 404 {
139- err : = controllererrors .NewErrorWithInnerError (
140- controllererrors .ErrorApplicationGatewayNotFound ,
132+ if response .Response .Response != nil {
133+ e = controllererrors .NewErrorWithInnerErrorf (
134+ controllererrors .ErrorApplicationGatewayUnexpectedStatusCode ,
141135 err ,
142- "received 404 NOT FOUND status code on getting Application Gateway from ARM." ,
136+ "Unexpected status code '%d' while performing a GET on Application Gateway." , response . Response . StatusCode ,
143137 )
144- glog .Error (err .Error ())
145- return utils .Retriable (false ), err
138+
139+ if response .Response .StatusCode == 404 {
140+ e .Code = controllererrors .ErrorApplicationGatewayNotFound
141+ }
142+
143+ if response .Response .StatusCode == 403 {
144+ e .Code = controllererrors .ErrorApplicationGatewayForbidden
145+
146+ clientID := "<agic-client-id>"
147+ if az .clientID != "" {
148+ clientID = az .clientID
149+ }
150+
151+ groupID := ResourceGroupID (az .subscriptionID , az .resourceGroupName )
152+ applicationGatewayID := ApplicationGatewayID (az .subscriptionID , az .resourceGroupName , az .appGwName )
153+ roleAssignmentCmd := fmt .Sprintf ("az role assignment create --role Reader --scope %s --assignee %s;" +
154+ " az role assignment create --role Contributor --scope %s --assignee %s" ,
155+ groupID ,
156+ clientID ,
157+ applicationGatewayID ,
158+ clientID ,
159+ )
160+
161+ e .Message += fmt .Sprintf (" You can use '%s' to assign permissions." +
162+ " AGIC Identity needs atleast has 'Contributor' access to Application Gateway '%s' and 'Reader' access to Application Gateway's Resource Group '%s'." ,
163+ roleAssignmentCmd ,
164+ string (az .appGwName ),
165+ string (az .resourceGroupName ),
166+ )
167+ }
146168 }
147169
148- if response .Response .Response != nil && response .Response .StatusCode != 200 {
149- // for example, getting 401. This is not expected as we are getting a token before making the call.
150- glog .Error ("unexpected ARM status code on GET existing App Gateway config: " , response .Response .StatusCode )
170+ glog .Errorf (e .Error ())
171+
172+ if controllererrors .IsErrorCode (e , controllererrors .ErrorApplicationGatewayNotFound ) {
173+ return utils .Retriable (false ), e
151174 }
152175
153- err := controllererrors .NewErrorWithInnerErrorf (
154- controllererrors .ErrorGetApplicationGatewayError ,
155- err ,
156- "failed fetching config for App Gateway instance. Will retry in %v." , retryPause ,
157- )
158- glog .Errorf (err .Error ())
159- return utils .Retriable (true ), err
176+ return utils .Retriable (true ), e
160177 })
161178
162- if err != nil && ! controllererrors .IsErrorCode (err , controllererrors .ErrorApplicationGatewayNotFound ) {
163- glog .Errorf ("Tried %d times to authenticate with ARM; Error: %s" , retryCount , err )
164- }
179+ return
180+ }
181+
182+ func (az * azClient ) GetGateway () (gateway n.ApplicationGateway , err error ) {
183+ err = utils .Retry (retryCount , retryPause ,
184+ func () (utils.Retriable , error ) {
185+ gateway , err = az .appGatewaysClient .Get (az .ctx , string (az .resourceGroupName ), string (az .appGwName ))
186+ if err != nil {
187+ glog .Errorf ("Error while getting application gateway '%s': %s" , string (az .appGwName ), err )
188+ }
189+ return utils .Retriable (true ), err
190+ })
165191 return
166192}
167193
@@ -459,7 +485,8 @@ func getTemplate() map[string]interface{} {
459485 "apiVersion": "2018-08-01",
460486 "location": "[resourceGroup().location]",
461487 "tags": {
462- "managed-by-k8s-ingress": "true"
488+ "managed-by-k8s-ingress": "true",
489+ "created-by": "ingress-appgw"
463490 },
464491 "properties": {
465492 "sku": {
0 commit comments