diff --git a/cmd/aro/operator.go b/cmd/aro/operator.go index 78eba80d284..9bc1b6c1e6a 100644 --- a/cmd/aro/operator.go +++ b/cmd/aro/operator.go @@ -130,8 +130,8 @@ func operator(ctx context.Context, log *logrus.Entry) error { if err = (checker.NewReconciler( log.WithField("controller", controllers.CheckerControllerName), - maocli, arocli, role, deploymentMode)).SetupWithManager(mgr); err != nil { - return fmt.Errorf("unable to create controller InternetChecker: %v", err) + maocli, arocli, kubernetescli, role, deploymentMode)).SetupWithManager(mgr); err != nil { + return fmt.Errorf("unable to create controller Checker: %v", err) } // +kubebuilder:scaffold:builder diff --git a/pkg/api/validate/dynamic.go b/pkg/api/validate/dynamic.go index 2a4965317ba..92da9d6ed83 100644 --- a/pkg/api/validate/dynamic.go +++ b/pkg/api/validate/dynamic.go @@ -18,7 +18,6 @@ import ( "k8s.io/apimachinery/pkg/util/wait" "github.com/Azure/ARO-RP/pkg/api" - "github.com/Azure/ARO-RP/pkg/env" "github.com/Azure/ARO-RP/pkg/util/azureclient/mgmt/authorization" "github.com/Azure/ARO-RP/pkg/util/azureclient/mgmt/network" utilpermissions "github.com/Azure/ARO-RP/pkg/util/permissions" @@ -29,27 +28,26 @@ import ( // SlimDynamic validate in the operator context. type SlimDynamic interface { - ValidateVnetPermissions(ctx context.Context) error - ValidateRouteTablesPermissions(ctx context.Context) error - ValidateVnetDns(ctx context.Context) error + ValidateVnetPermissions(ctx context.Context, code string, typ string) error + ValidateRouteTablesPermissions(ctx context.Context, code string, typ string) error + ValidateVnetDNS(ctx context.Context) error // etc // does Quota code go in here too? } +var _ SlimDynamic = (*dynamic)(nil) + type dynamic struct { log *logrus.Entry vnetr *azure.Resource masterSubnetID string workerSubnetIDs []string - code string - typ string - permissions authorization.PermissionsClient virtualNetworks virtualNetworksGetClient } -func NewValidator(log *logrus.Entry, env env.Interface, masterSubnetID string, workerSubnetIDs []string, subscriptionID string, authorizer refreshable.Authorizer, code string, typ string) (*dynamic, error) { +func NewValidator(log *logrus.Entry, azEnv *azure.Environment, masterSubnetID string, workerSubnetIDs []string, subscriptionID string, authorizer refreshable.Authorizer) (*dynamic, error) { vnetID, _, err := subnet.Split(masterSubnetID) if err != nil { return nil, err @@ -66,16 +64,25 @@ func NewValidator(log *logrus.Entry, env env.Interface, masterSubnetID string, w masterSubnetID: masterSubnetID, workerSubnetIDs: workerSubnetIDs, - code: code, - typ: typ, - - permissions: authorization.NewPermissionsClient(env.Environment(), subscriptionID, authorizer), - virtualNetworks: newVirtualNetworksCache(network.NewVirtualNetworksClient(env.Environment(), subscriptionID, authorizer)), + permissions: authorization.NewPermissionsClient(azEnv, subscriptionID, authorizer), + virtualNetworks: newVirtualNetworksCache(network.NewVirtualNetworksClient(azEnv, subscriptionID, authorizer)), }, nil } -func (dv *dynamic) ValidateVnetPermissions(ctx context.Context) error { - dv.log.Printf("ValidateVnetPermissions (%s)", dv.typ) +func NewSlimDynamicValidator(log *logrus.Entry, azEnv *azure.Environment, vnetResource azure.Resource, masterSubnetID string, workerSubnetsIDs []string, subscriptionID string, authorizer refreshable.Authorizer) SlimDynamic { + + return &dynamic{ + log: log, + vnetr: &vnetResource, + masterSubnetID: masterSubnetID, + workerSubnetIDs: workerSubnetsIDs, + permissions: authorization.NewPermissionsClient(azEnv, subscriptionID, authorizer), + virtualNetworks: newVirtualNetworksCache(network.NewVirtualNetworksClient(azEnv, subscriptionID, authorizer)), + } + +} +func (dv *dynamic) ValidateVnetPermissions(ctx context.Context, code string, typ string) error { + dv.log.Printf("ValidateVnetPermissions (%s)", typ) err := dv.validateActions(ctx, dv.vnetr, []string{ "Microsoft.Network/virtualNetworks/join/action", @@ -87,7 +94,7 @@ func (dv *dynamic) ValidateVnetPermissions(ctx context.Context) error { }) if err == wait.ErrWaitTimeout { - return api.NewCloudError(http.StatusBadRequest, dv.code, "", "The %s does not have Network Contributor permission on vnet '%s'.", dv.typ, dv.vnetr) + return api.NewCloudError(http.StatusBadRequest, code, "", "The %s does not have Network Contributor permission on vnet '%s'.", typ, dv.vnetr) } if detailedErr, ok := err.(autorest.DetailedError); ok && detailedErr.StatusCode == http.StatusNotFound { @@ -96,7 +103,7 @@ func (dv *dynamic) ValidateVnetPermissions(ctx context.Context) error { return err } -func (dv *dynamic) ValidateRouteTablesPermissions(ctx context.Context) error { +func (dv *dynamic) ValidateRouteTablesPermissions(ctx context.Context, code string, typ string) error { vnet, err := dv.virtualNetworks.Get(ctx, dv.vnetr.ResourceGroup, dv.vnetr.ResourceName, "") if err != nil { return err @@ -135,7 +142,7 @@ func (dv *dynamic) ValidateRouteTablesPermissions(ctx context.Context) error { sort.Slice(rts, func(i, j int) bool { return strings.Compare(m[rts[i]], m[rts[j]]) < 0 }) for _, rt := range rts { - err := dv.validateRouteTablePermissions(ctx, rt, m[rt]) + err := dv.validateRouteTablePermissions(ctx, rt, m[rt], code, typ) if err != nil { return err } @@ -144,8 +151,8 @@ func (dv *dynamic) ValidateRouteTablesPermissions(ctx context.Context) error { return nil } -func (dv *dynamic) validateRouteTablePermissions(ctx context.Context, rtID string, path string) error { - dv.log.Printf("validateRouteTablePermissions(%s, %s)", dv.typ, path) +func (dv *dynamic) validateRouteTablePermissions(ctx context.Context, rtID string, path string, code string, typ string) error { + dv.log.Printf("validateRouteTablePermissions(%s, %s)", typ, path) rtr, err := azure.ParseResourceID(rtID) if err != nil { @@ -158,7 +165,7 @@ func (dv *dynamic) validateRouteTablePermissions(ctx context.Context, rtID strin "Microsoft.Network/routeTables/write", }) if err == wait.ErrWaitTimeout { - return api.NewCloudError(http.StatusBadRequest, dv.code, "", "The %s does not have Network Contributor permission on route table '%s'.", dv.typ, rtID) + return api.NewCloudError(http.StatusBadRequest, code, "", "The %s does not have Network Contributor permission on route table '%s'.", typ, rtID) } if detailedErr, ok := err.(autorest.DetailedError); ok && detailedErr.StatusCode == http.StatusNotFound { diff --git a/pkg/api/validate/dynamic_test.go b/pkg/api/validate/dynamic_test.go index 5489a0b9200..fe7455d3f1e 100644 --- a/pkg/api/validate/dynamic_test.go +++ b/pkg/api/validate/dynamic_test.go @@ -18,6 +18,7 @@ import ( "github.com/golang/mock/gomock" "github.com/sirupsen/logrus" + "github.com/Azure/ARO-RP/pkg/api" mock_authorization "github.com/Azure/ARO-RP/pkg/util/mocks/azureclient/mgmt/authorization" mock_network "github.com/Azure/ARO-RP/pkg/util/mocks/azureclient/mgmt/network" ) @@ -115,11 +116,9 @@ func TestValidateVnetPermissions(t *testing.T) { ResourceName: vnetName, SubscriptionID: subscriptionID, }, - code: "InvalidResourceProviderPermissions", - typ: "resource provider", } - err := dv.ValidateVnetPermissions(ctx) + err := dv.ValidateVnetPermissions(ctx, api.CloudErrorCodeInvalidResourceProviderPermissions, "resource provider") if err != nil && err.Error() != tt.wantErr || err == nil && tt.wantErr != "" { t.Error(err) @@ -347,12 +346,10 @@ func TestValidateRouteTablePermissions(t *testing.T) { dv := &dynamic{ log: logrus.NewEntry(logrus.StandardLogger()), permissions: permissionsClient, - code: "InvalidResourceProviderPermissions", - typ: "resource provider", } // purposefully hardcoding path to "" so it is not needed in the wantErr message - err := dv.validateRouteTablePermissions(ctx, tt.rtID, "") + err := dv.validateRouteTablePermissions(ctx, tt.rtID, "", api.CloudErrorCodeInvalidResourceProviderPermissions, "resource provider") if err != nil && err.Error() != tt.wantErr || err == nil && tt.wantErr != "" { t.Error(err) @@ -506,9 +503,6 @@ func TestValidateRouteTablesPermissions(t *testing.T) { masterSubnetID: masterSubnet, workerSubnetIDs: []string{workerSubnet}, - - code: "InvalidResourceProviderPermissions", - typ: "resource provider", } if tt.permissionMocks != nil { @@ -519,7 +513,7 @@ func TestValidateRouteTablesPermissions(t *testing.T) { tt.vnetMocks(vnetClient, *vnet) } - err := dv.ValidateRouteTablesPermissions(ctx) + err := dv.ValidateRouteTablesPermissions(ctx, api.CloudErrorCodeInvalidResourceProviderPermissions, "resource provider") if err != nil && err.Error() != tt.wantErr || err == nil && tt.wantErr != "" { t.Error(err) diff --git a/pkg/api/validate/openshiftcluster_validatedynamic.go b/pkg/api/validate/openshiftcluster_validatedynamic.go index 5dd2333d575..c3e0900352e 100644 --- a/pkg/api/validate/openshiftcluster_validatedynamic.go +++ b/pkg/api/validate/openshiftcluster_validatedynamic.go @@ -42,11 +42,11 @@ func NewOpenShiftClusterDynamicValidator(log *logrus.Entry, env env.Interface, o } } -type azureClaim struct { +type AzureClaim struct { Roles []string `json:"roles,omitempty"` } -func (*azureClaim) Valid() error { +func (*AzureClaim) Valid() error { return fmt.Errorf("unimplemented") } @@ -71,17 +71,17 @@ func (dv *openShiftClusterDynamicValidator) Dynamic(ctx context.Context) error { } // FP validation - fpDynamic, err := NewValidator(dv.log, dv.env, mSubnetID, wSubnetIDs, dv.subscriptionDoc.ID, dv.fpAuthorizer, api.CloudErrorCodeInvalidResourceProviderPermissions, "resource provider") + fpDynamic, err := NewValidator(dv.log, dv.env.Environment(), mSubnetID, wSubnetIDs, dv.subscriptionDoc.ID, dv.fpAuthorizer) if err != nil { return err } - err = fpDynamic.ValidateVnetPermissions(ctx) + err = fpDynamic.ValidateVnetPermissions(ctx, api.CloudErrorCodeInvalidResourceProviderPermissions, "resource provider") if err != nil { return err } - err = fpDynamic.ValidateRouteTablesPermissions(ctx) + err = fpDynamic.ValidateRouteTablesPermissions(ctx, api.CloudErrorCodeInvalidResourceProviderPermissions, "resource provider") if err != nil { return err } @@ -92,24 +92,24 @@ func (dv *openShiftClusterDynamicValidator) Dynamic(ctx context.Context) error { return err } - token, err := aad.GetToken(ctx, dv.log, dv.oc, dv.subscriptionDoc, dv.env.Environment().ActiveDirectoryEndpoint, dv.env.Environment().ResourceManagerEndpoint) + token, err := aad.GetToken(ctx, dv.log, dv.oc.Properties.ServicePrincipalProfile.ClientID, dv.oc.Properties.ServicePrincipalProfile.ClientSecret, dv.subscriptionDoc.Subscription.Properties.TenantID, dv.env.Environment().ActiveDirectoryEndpoint, dv.env.Environment().ResourceManagerEndpoint) if err != nil { return err } spAuthorizer := refreshable.NewAuthorizer(token) - spDynamic, err := NewValidator(dv.log, dv.env, mSubnetID, wSubnetIDs, dv.subscriptionDoc.ID, spAuthorizer, api.CloudErrorCodeInvalidServicePrincipalPermissions, "provided service principal") + spDynamic, err := NewValidator(dv.log, dv.env.Environment(), mSubnetID, wSubnetIDs, dv.subscriptionDoc.ID, spAuthorizer) if err != nil { return err } - err = spDynamic.ValidateVnetPermissions(ctx) + err = spDynamic.ValidateVnetPermissions(ctx, api.CloudErrorCodeInvalidServicePrincipalPermissions, "provided service principal") if err != nil { return err } - err = spDynamic.ValidateRouteTablesPermissions(ctx) + err = spDynamic.ValidateRouteTablesPermissions(ctx, api.CloudErrorCodeInvalidServicePrincipalPermissions, "provided service principal") if err != nil { return err } @@ -300,13 +300,13 @@ func validateServicePrincipalProfile(ctx context.Context, log *logrus.Entry, env log.Print("validateServicePrincipalProfile") - token, err := aad.GetToken(ctx, log, oc, sub, env.Environment().ActiveDirectoryEndpoint, env.Environment().GraphEndpoint) + token, err := aad.GetToken(ctx, log, oc.Properties.ServicePrincipalProfile.ClientID, oc.Properties.ServicePrincipalProfile.ClientSecret, sub.Subscription.Properties.TenantID, env.Environment().ActiveDirectoryEndpoint, env.Environment().ResourceManagerEndpoint) if err != nil { return err } p := &jwt.Parser{} - c := &azureClaim{} + c := &AzureClaim{} _, _, err = p.ParseUnverified(token.OAuthToken(), c) if err != nil { return err diff --git a/pkg/api/validate/quota.go b/pkg/api/validate/quota.go index 41be9d3892a..dce9d9c33ce 100644 --- a/pkg/api/validate/quota.go +++ b/pkg/api/validate/quota.go @@ -108,7 +108,7 @@ func NewAzureQuotaValidator(ctx context.Context, log *logrus.Entry, env env.Inte return nil, err } - token, err := aad.GetToken(ctx, log, oc, subscriptionDoc, env.Environment().ActiveDirectoryEndpoint, env.Environment().ResourceManagerEndpoint) + token, err := aad.GetToken(ctx, log, oc.Properties.ServicePrincipalProfile.ClientID, oc.Properties.ServicePrincipalProfile.ClientSecret, subscriptionDoc.Subscription.Properties.TenantID, env.Environment().ActiveDirectoryEndpoint, env.Environment().ResourceManagerEndpoint) if err != nil { return nil, err } diff --git a/pkg/cluster/deploystorage.go b/pkg/cluster/deploystorage.go index 718c736fef4..b08a16feed5 100644 --- a/pkg/cluster/deploystorage.go +++ b/pkg/cluster/deploystorage.go @@ -42,7 +42,7 @@ func (m *manager) clusterSPObjectID(ctx context.Context) (string, error) { var clusterSPObjectID string spp := &m.doc.OpenShiftCluster.Properties.ServicePrincipalProfile - token, err := aad.GetToken(ctx, m.log, m.doc.OpenShiftCluster, m.subscriptionDoc, m.env.Environment().ActiveDirectoryEndpoint, m.env.Environment().GraphEndpoint) + token, err := aad.GetToken(ctx, m.log, m.doc.OpenShiftCluster.Properties.ServicePrincipalProfile.ClientID, m.doc.OpenShiftCluster.Properties.ServicePrincipalProfile.ClientSecret, m.subscriptionDoc.Subscription.Properties.TenantID, m.env.Environment().ActiveDirectoryEndpoint, m.env.Environment().GraphEndpoint) if err != nil { return "", err } diff --git a/pkg/operator/apis/aro.openshift.io/v1alpha1/cluster_types.go b/pkg/operator/apis/aro.openshift.io/v1alpha1/cluster_types.go index 1be22e4b221..62fbfe3ddc2 100644 --- a/pkg/operator/apis/aro.openshift.io/v1alpha1/cluster_types.go +++ b/pkg/operator/apis/aro.openshift.io/v1alpha1/cluster_types.go @@ -13,10 +13,11 @@ const ( InternetReachableFromMaster status.ConditionType = "InternetReachableFromMaster" InternetReachableFromWorker status.ConditionType = "InternetReachableFromWorker" MachineValid status.ConditionType = "MachineValid" + DNSValid status.ConditionType = "DNSValid" ) func AllConditionTypes() []status.ConditionType { - return []status.ConditionType{InternetReachableFromMaster, InternetReachableFromWorker, MachineValid} + return []status.ConditionType{InternetReachableFromMaster, InternetReachableFromWorker, MachineValid, DNSValid} } type GenevaLoggingSpec struct { diff --git a/pkg/operator/controllers/checker/auth.go b/pkg/operator/controllers/checker/auth.go new file mode 100644 index 00000000000..09085d919ff --- /dev/null +++ b/pkg/operator/controllers/checker/auth.go @@ -0,0 +1,66 @@ +package checker + +// Copyright (c) Microsoft Corporation. +// Licensed under the Apache License 2.0. + +import ( + "context" + "errors" + "net/http" + + "github.com/Azure/go-autorest/autorest/adal" + jwt "github.com/form3tech-oss/jwt-go" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + + "github.com/Azure/ARO-RP/pkg/api" + "github.com/Azure/ARO-RP/pkg/api/validate" + "github.com/Azure/ARO-RP/pkg/util/refreshable" +) + +type credentials struct { + clientID []byte + clientSecret []byte + tenantID []byte +} + +func newAuthorizer(token *adal.ServicePrincipalToken) (refreshable.Authorizer, error) { + p := &jwt.Parser{} + c := &validate.AzureClaim{} + _, _, err := p.ParseUnverified(token.OAuthToken(), c) + if err != nil { + return nil, err + } + + for _, role := range c.Roles { + if role == "Application.ReadWrite.OwnedBy" { + return nil, api.NewCloudError(http.StatusBadRequest, api.CloudErrorCodeInvalidServicePrincipalCredentials, "properties.servicePrincipalProfile", "The provided service principal must not have the Application.ReadWrite.OwnedBy permission.") + } + } + + return refreshable.NewAuthorizer(token), nil +} + +func azCredentials(ctx context.Context, kubernetescli kubernetes.Interface) (*credentials, error) { + var creds credentials + mysec, err := kubernetescli.CoreV1().Secrets(azureCredentialSecretNameSpace).Get(ctx, azureCredentialSecretName, metav1.GetOptions{}) + if err != nil { + return nil, err + } + if _, ok := mysec.Data["azure_client_id"]; !ok { + return nil, errors.New("azure_client_id does not exists") + } + creds.clientID = mysec.Data["azure_client_id"] + + if _, ok := mysec.Data["azure_client_secret"]; !ok { + return nil, errors.New("azure_client_secret does not exists") + } + creds.clientSecret = mysec.Data["azure_client_secret"] + + if _, ok := mysec.Data["azure_tenant_id"]; !ok { + return nil, errors.New("azure_tenant_id does not exists") + } + creds.tenantID = mysec.Data["azure_tenant_id"] + + return &creds, nil +} diff --git a/pkg/operator/controllers/checker/checker_controller.go b/pkg/operator/controllers/checker/checker_controller.go index efcb498b6cb..eeb9c3343c3 100644 --- a/pkg/operator/controllers/checker/checker_controller.go +++ b/pkg/operator/controllers/checker/checker_controller.go @@ -10,6 +10,7 @@ import ( machinev1beta1 "github.com/openshift/machine-api-operator/pkg/apis/machine/v1beta1" maoclient "github.com/openshift/machine-api-operator/pkg/generated/clientset/versioned" "github.com/sirupsen/logrus" + "k8s.io/client-go/kubernetes" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/reconcile" @@ -29,11 +30,11 @@ type CheckerController struct { checkers []Checker } -func NewReconciler(log *logrus.Entry, maocli maoclient.Interface, arocli aroclient.Interface, role string, deploymentMode deployment.Mode) *CheckerController { +func NewReconciler(log *logrus.Entry, maocli maoclient.Interface, arocli aroclient.Interface, kubernetescli kubernetes.Interface, role string, deploymentMode deployment.Mode) *CheckerController { checkers := []Checker{NewInternetChecker(log, arocli, role)} if role == operator.RoleMaster { - checkers = append(checkers, NewMachineChecker(log, maocli, arocli, role, deploymentMode)) + checkers = append(checkers, NewMachineChecker(log, maocli, arocli, role, deploymentMode), NewDNSChecker(log, arocli, kubernetescli, maocli, role)) } return &CheckerController{ diff --git a/pkg/operator/controllers/checker/const.go b/pkg/operator/controllers/checker/const.go new file mode 100644 index 00000000000..2fd84230964 --- /dev/null +++ b/pkg/operator/controllers/checker/const.go @@ -0,0 +1,10 @@ +package checker + +// Copyright (c) Microsoft Corporation. +// Licensed under the Apache License 2.0. + +const ( + azureCredentialSecretName = "azure-credentials" + azureCredentialSecretNameSpace = "kube-system" + machineSetsNamespace = "openshift-machine-api" +) diff --git a/pkg/operator/controllers/checker/dnschecker.go b/pkg/operator/controllers/checker/dnschecker.go new file mode 100644 index 00000000000..2f5d71a287d --- /dev/null +++ b/pkg/operator/controllers/checker/dnschecker.go @@ -0,0 +1,111 @@ +package checker + +// Copyright (c) Microsoft Corporation. +// Licensed under the Apache License 2.0. + +import ( + "context" + + "github.com/Azure/go-autorest/autorest/azure" + maoclient "github.com/openshift/machine-api-operator/pkg/generated/clientset/versioned" + "github.com/operator-framework/operator-sdk/pkg/status" + "github.com/sirupsen/logrus" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + + "github.com/Azure/ARO-RP/pkg/api" + "github.com/Azure/ARO-RP/pkg/api/validate" + arov1alpha "github.com/Azure/ARO-RP/pkg/operator/apis/aro.openshift.io/v1alpha1" + aroclient "github.com/Azure/ARO-RP/pkg/operator/clientset/versioned" + "github.com/Azure/ARO-RP/pkg/operator/controllers" + "github.com/Azure/ARO-RP/pkg/util/aad" +) + +type DNSChecker struct { + arocli aroclient.Interface + log *logrus.Entry + kubernetescli kubernetes.Interface + clustercli maoclient.Interface + role string +} + +func NewDNSChecker(log *logrus.Entry, arocli aroclient.Interface, kubernetescli kubernetes.Interface, clustercli maoclient.Interface, role string) *DNSChecker { + return &DNSChecker{ + log: log, + arocli: arocli, + kubernetescli: kubernetescli, + clustercli: clustercli, + role: role, + } +} + +func (d *DNSChecker) Name() string { + return "DNSChecker" +} + +func (d *DNSChecker) Check(ctx context.Context) error { + instance, err := d.arocli.AroV1alpha1().Clusters().Get(ctx, arov1alpha.SingletonClusterName, metav1.GetOptions{}) + if err != nil { + return err + } + //Get masterSubnet from master machine + masterSubnet, err := masterSubnetId(ctx, d.clustercli, instance.Spec.VnetID) + if err != nil { + return err + } + //Get endpoints from operator + azEnv, err := azure.EnvironmentFromName(instance.Spec.AZEnvironment) + if err != nil { + return err + } + //Grab azure-credentials from secret + credentials, err := azCredentials(ctx, d.kubernetescli) + if err != nil { + return err + } + //create service principal token from azure-credentials + token, err := aad.GetToken(ctx, d.log, string(credentials.clientID), api.SecureString(credentials.clientSecret), string(credentials.tenantID), azEnv.ActiveDirectoryEndpoint, azEnv.ResourceManagerEndpoint) + if err != nil { + return err + } + //create refreshable authorizer from token + authorizer, err := newAuthorizer(token) + if err != nil { + return err + } + resource, err := azure.ParseResourceID(instance.Spec.ResourceID) + if err != nil { + return err + } + + vnetResource, err := azure.ParseResourceID(instance.Spec.VnetID) + if err != nil { + return err + } + checker := validate.NewSlimDynamicValidator(d.log, &azEnv, vnetResource, masterSubnet, nil, resource.SubscriptionID, authorizer) + + var condition *status.Condition + + err = checker.ValidateVnetDNS(ctx) + if err != nil { + condition = &status.Condition{ + Type: arov1alpha.DNSValid, + Status: corev1.ConditionFalse, + Message: "Custom DNS Found on VNET", + Reason: "CheckFailed", + } + } else { + condition = &status.Condition{ + Type: arov1alpha.DNSValid, + Status: corev1.ConditionTrue, + Message: "DNS Check successful", + Reason: "CheckDone", + } + } + err = controllers.SetCondition(ctx, d.arocli, condition, d.role) + if err != nil { + return err + } + return nil +} diff --git a/pkg/operator/controllers/checker/machinechecker.go b/pkg/operator/controllers/checker/machinechecker.go index 96a0451f6e3..fc9318811cc 100644 --- a/pkg/operator/controllers/checker/machinechecker.go +++ b/pkg/operator/controllers/checker/machinechecker.go @@ -26,10 +26,6 @@ import ( _ "github.com/Azure/ARO-RP/pkg/util/scheme" ) -const ( - machineSetsNamespace = "openshift-machine-api" -) - // MachineChecker reconciles the alertmanager webhook type MachineChecker struct { clustercli maoclient.Interface diff --git a/pkg/operator/controllers/checker/subnets.go b/pkg/operator/controllers/checker/subnets.go new file mode 100644 index 00000000000..f3c9bd33af3 --- /dev/null +++ b/pkg/operator/controllers/checker/subnets.go @@ -0,0 +1,42 @@ +package checker + +// Copyright (c) Microsoft Corporation. +// Licensed under the Apache License 2.0. + +import ( + "context" + "fmt" + + maoclient "github.com/openshift/machine-api-operator/pkg/generated/clientset/versioned" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes/scheme" + azureproviderv1beta1 "sigs.k8s.io/cluster-api-provider-azure/pkg/apis/azureprovider/v1beta1" +) + +func masterSubnetId(ctx context.Context, clustercli maoclient.Interface, vnetID string) (string, error) { + var masterSubnet string + + machines, err := clustercli.MachineV1beta1().Machines(machineSetsNamespace).List(ctx, metav1.ListOptions{}) + if err != nil { + return masterSubnet, err + } + + for _, machine := range machines.Items { + if isMaster, err := isMasterRole(&machine); err != nil && isMaster { + o, _, err := scheme.Codecs.UniversalDeserializer().Decode(machine.Spec.ProviderSpec.Value.Raw, nil, nil) + if err != nil { + return masterSubnet, err + } + + machineProviderSpec, ok := o.(*azureproviderv1beta1.AzureMachineProviderSpec) + if !ok { + return masterSubnet, fmt.Errorf("machine %s: failed to read provider spec: %T", machine.Name, o) + } + masterSubnet = machineProviderSpec.Subnet + break + } + } + result := fmt.Sprintf("%s/subnets/%s", vnetID, masterSubnet) + + return result, nil +} diff --git a/pkg/util/aad/aad.go b/pkg/util/aad/aad.go index 26f36f74c70..c322d4c73f2 100644 --- a/pkg/util/aad/aad.go +++ b/pkg/util/aad/aad.go @@ -20,13 +20,12 @@ import ( // GetToken authenticates in the customer's tenant as the cluster service // principal and returns a token. -func GetToken(ctx context.Context, log *logrus.Entry, oc *api.OpenShiftCluster, subscriptionDoc *api.SubscriptionDocument, aadEndpoint, resource string) (*adal.ServicePrincipalToken, error) { - spp := &oc.Properties.ServicePrincipalProfile +func GetToken(ctx context.Context, log *logrus.Entry, clientID string, clientSecret api.SecureString, tenantID string, aadEndpoint, resource string) (*adal.ServicePrincipalToken, error) { conf := auth.ClientCredentialsConfig{ - ClientID: spp.ClientID, - ClientSecret: string(spp.ClientSecret), - TenantID: subscriptionDoc.Subscription.Properties.TenantID, + ClientID: clientID, + ClientSecret: string(clientSecret), + TenantID: tenantID, Resource: resource, AADEndpoint: aadEndpoint, } diff --git a/vendor/github.com/dimchansky/utfbom/go.mod b/vendor/github.com/dimchansky/utfbom/go.mod index 8f8620af3b5..f86fb1fb040 100644 --- a/vendor/github.com/dimchansky/utfbom/go.mod +++ b/vendor/github.com/dimchansky/utfbom/go.mod @@ -1 +1,3 @@ module github.com/dimchansky/utfbom + +go 1.15 diff --git a/vendor/github.com/ghodss/yaml/go.mod b/vendor/github.com/ghodss/yaml/go.mod index 8d9ad7b6406..e071478c4b1 100644 --- a/vendor/github.com/ghodss/yaml/go.mod +++ b/vendor/github.com/ghodss/yaml/go.mod @@ -1,3 +1,5 @@ module github.com/ghodss/yaml +go 1.15 + require gopkg.in/yaml.v2 v2.2.2 diff --git a/vendor/github.com/go-toolsmith/astcast/go.mod b/vendor/github.com/go-toolsmith/astcast/go.mod index 3e431993e3b..278d9313a40 100644 --- a/vendor/github.com/go-toolsmith/astcast/go.mod +++ b/vendor/github.com/go-toolsmith/astcast/go.mod @@ -1,5 +1,7 @@ module github.com/go-toolsmith/astcast +go 1.15 + require ( github.com/go-toolsmith/astequal v1.0.0 // indirect github.com/go-toolsmith/strparse v1.0.0 diff --git a/vendor/github.com/go-toolsmith/astcopy/go.mod b/vendor/github.com/go-toolsmith/astcopy/go.mod index 6f3b3027a8b..de72d26aad4 100644 --- a/vendor/github.com/go-toolsmith/astcopy/go.mod +++ b/vendor/github.com/go-toolsmith/astcopy/go.mod @@ -1,5 +1,7 @@ module github.com/go-toolsmith/astcopy +go 1.15 + require ( github.com/go-toolsmith/astequal v1.0.0 github.com/go-toolsmith/strparse v1.0.0 diff --git a/vendor/github.com/go-toolsmith/astequal/go.mod b/vendor/github.com/go-toolsmith/astequal/go.mod index 86fa407721b..090b280a0ed 100644 --- a/vendor/github.com/go-toolsmith/astequal/go.mod +++ b/vendor/github.com/go-toolsmith/astequal/go.mod @@ -1 +1,3 @@ module github.com/go-toolsmith/astequal + +go 1.15 diff --git a/vendor/github.com/go-toolsmith/astfmt/go.mod b/vendor/github.com/go-toolsmith/astfmt/go.mod index d23db156624..21c6172d326 100644 --- a/vendor/github.com/go-toolsmith/astfmt/go.mod +++ b/vendor/github.com/go-toolsmith/astfmt/go.mod @@ -1,5 +1,7 @@ module github.com/go-toolsmith/astfmt +go 1.15 + require ( github.com/go-toolsmith/astequal v1.0.0 // indirect github.com/go-toolsmith/strparse v1.0.0 diff --git a/vendor/github.com/go-toolsmith/astp/go.mod b/vendor/github.com/go-toolsmith/astp/go.mod index 023a09392f9..fa0d500df4a 100644 --- a/vendor/github.com/go-toolsmith/astp/go.mod +++ b/vendor/github.com/go-toolsmith/astp/go.mod @@ -1,5 +1,7 @@ module github.com/go-toolsmith/astp +go 1.15 + require ( github.com/go-toolsmith/astequal v1.0.0 // indirect github.com/go-toolsmith/strparse v1.0.0 diff --git a/vendor/github.com/go-toolsmith/strparse/go.mod b/vendor/github.com/go-toolsmith/strparse/go.mod index ed9d8813666..3a772a86ec6 100644 --- a/vendor/github.com/go-toolsmith/strparse/go.mod +++ b/vendor/github.com/go-toolsmith/strparse/go.mod @@ -1 +1,3 @@ module github.com/go-toolsmith/strparse + +go 1.15 diff --git a/vendor/github.com/go-toolsmith/typep/go.mod b/vendor/github.com/go-toolsmith/typep/go.mod index 197a57d3068..27bf8d16af3 100644 --- a/vendor/github.com/go-toolsmith/typep/go.mod +++ b/vendor/github.com/go-toolsmith/typep/go.mod @@ -1 +1,3 @@ module github.com/go-toolsmith/typep + +go 1.15 diff --git a/vendor/github.com/golangci/lint-1/go.mod b/vendor/github.com/golangci/lint-1/go.mod index fafbd340b3e..b6016d913da 100644 --- a/vendor/github.com/golangci/lint-1/go.mod +++ b/vendor/github.com/golangci/lint-1/go.mod @@ -1,3 +1,5 @@ module github.com/golangci/lint-1 +go 1.15 + require golang.org/x/tools v0.0.0-20190311212946-11955173bddd diff --git a/vendor/github.com/google/uuid/go.mod b/vendor/github.com/google/uuid/go.mod index fc84cd79d4c..82a28edae9e 100644 --- a/vendor/github.com/google/uuid/go.mod +++ b/vendor/github.com/google/uuid/go.mod @@ -1 +1,3 @@ module github.com/google/uuid + +go 1.15 diff --git a/vendor/github.com/googleapis/gax-go/v2/go.mod b/vendor/github.com/googleapis/gax-go/v2/go.mod index 9cdfaf44752..862f0ee0dad 100644 --- a/vendor/github.com/googleapis/gax-go/v2/go.mod +++ b/vendor/github.com/googleapis/gax-go/v2/go.mod @@ -1,3 +1,5 @@ module github.com/googleapis/gax-go/v2 +go 1.15 + require google.golang.org/grpc v1.19.0 diff --git a/vendor/github.com/gorilla/sessions/go.mod b/vendor/github.com/gorilla/sessions/go.mod index 9028bcf1c85..ef0fa2a0586 100644 --- a/vendor/github.com/gorilla/sessions/go.mod +++ b/vendor/github.com/gorilla/sessions/go.mod @@ -1,3 +1,5 @@ module github.com/gorilla/sessions +go 1.15 + require github.com/gorilla/securecookie v1.1.1 diff --git a/vendor/github.com/hashicorp/hcl/go.mod b/vendor/github.com/hashicorp/hcl/go.mod index 4debbbe3580..4c78c89d100 100644 --- a/vendor/github.com/hashicorp/hcl/go.mod +++ b/vendor/github.com/hashicorp/hcl/go.mod @@ -1,3 +1,5 @@ module github.com/hashicorp/hcl +go 1.15 + require github.com/davecgh/go-spew v1.1.1 diff --git a/vendor/github.com/kisielk/gotool/go.mod b/vendor/github.com/kisielk/gotool/go.mod index 503b37c6fb1..75407034b14 100644 --- a/vendor/github.com/kisielk/gotool/go.mod +++ b/vendor/github.com/kisielk/gotool/go.mod @@ -1 +1,3 @@ -module "github.com/kisielk/gotool" +module github.com/kisielk/gotool + +go 1.15 diff --git a/vendor/github.com/magiconair/properties/go.mod b/vendor/github.com/magiconair/properties/go.mod index 02a6f86557f..0e933369d44 100644 --- a/vendor/github.com/magiconair/properties/go.mod +++ b/vendor/github.com/magiconair/properties/go.mod @@ -1 +1,3 @@ module github.com/magiconair/properties + +go 1.15 diff --git a/vendor/github.com/mitchellh/go-homedir/go.mod b/vendor/github.com/mitchellh/go-homedir/go.mod index 7efa09a0432..5ff64a064aa 100644 --- a/vendor/github.com/mitchellh/go-homedir/go.mod +++ b/vendor/github.com/mitchellh/go-homedir/go.mod @@ -1 +1,3 @@ module github.com/mitchellh/go-homedir + +go 1.15 diff --git a/vendor/github.com/pborman/uuid/go.mod b/vendor/github.com/pborman/uuid/go.mod index 099fc7de0d5..f58aea102d4 100644 --- a/vendor/github.com/pborman/uuid/go.mod +++ b/vendor/github.com/pborman/uuid/go.mod @@ -1,3 +1,5 @@ module github.com/pborman/uuid +go 1.15 + require github.com/google/uuid v1.0.0 diff --git a/vendor/github.com/spf13/afero/go.mod b/vendor/github.com/spf13/afero/go.mod index 08685509957..1961f905ee4 100644 --- a/vendor/github.com/spf13/afero/go.mod +++ b/vendor/github.com/spf13/afero/go.mod @@ -1,3 +1,5 @@ module github.com/spf13/afero +go 1.15 + require golang.org/x/text v0.3.0 diff --git a/vendor/github.com/spf13/cast/go.mod b/vendor/github.com/spf13/cast/go.mod index c1c0232dd93..d632f765dd2 100644 --- a/vendor/github.com/spf13/cast/go.mod +++ b/vendor/github.com/spf13/cast/go.mod @@ -1,5 +1,7 @@ module github.com/spf13/cast +go 1.15 + require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect diff --git a/vendor/github.com/spf13/jwalterweatherman/go.mod b/vendor/github.com/spf13/jwalterweatherman/go.mod index 1dbcfd3e889..c642520b9b6 100644 --- a/vendor/github.com/spf13/jwalterweatherman/go.mod +++ b/vendor/github.com/spf13/jwalterweatherman/go.mod @@ -1,5 +1,7 @@ module github.com/spf13/jwalterweatherman +go 1.15 + require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect diff --git a/vendor/github.com/ugorji/go/codec/go.sum b/vendor/github.com/ugorji/go/codec/go.sum new file mode 100644 index 00000000000..9e57d0fa054 --- /dev/null +++ b/vendor/github.com/ugorji/go/codec/go.sum @@ -0,0 +1,2 @@ +github.com/ugorji/go v1.2.3/go.mod h1:5l8GZ8hZvmL4uMdy+mhCO1LjswGRYco9Q3HfuisB21A= +github.com/ugorji/go/codec v1.2.3/go.mod h1:5FxzDJIgeiWJZslYHPj+LS1dq1ZBQVelZFnjsFGI/Uc= diff --git a/vendor/gopkg.in/yaml.v2/go.sum b/vendor/gopkg.in/yaml.v2/go.sum new file mode 100644 index 00000000000..bfc2806cb2a --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/go.sum @@ -0,0 +1 @@ +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/vendor/gopkg.in/yaml.v3/go.mod b/vendor/gopkg.in/yaml.v3/go.mod index f407ea3213e..ab9a7cb834a 100644 --- a/vendor/gopkg.in/yaml.v3/go.mod +++ b/vendor/gopkg.in/yaml.v3/go.mod @@ -1,5 +1,5 @@ -module "gopkg.in/yaml.v3" +module gopkg.in/yaml.v3 -require ( - "gopkg.in/check.v1" v0.0.0-20161208181325-20d25e280405 -) +go 1.15 + +require gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 diff --git a/vendor/gopkg.in/yaml.v3/go.sum b/vendor/gopkg.in/yaml.v3/go.sum new file mode 100644 index 00000000000..bfc2806cb2a --- /dev/null +++ b/vendor/gopkg.in/yaml.v3/go.sum @@ -0,0 +1 @@ +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=