Skip to content
This repository has been archived by the owner on Oct 10, 2023. It is now read-only.

Extend AntreaConfig to Support Antrea-NSX Registration Workflow #3816

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
210 changes: 209 additions & 1 deletion addons/controllers/antrea/antreaconfig_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,17 @@ package controllers
import (
"context"
"fmt"
"strings"

"github.com/go-logr/logr"
yaml "gopkg.in/yaml.v3"
corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
vsphere "sigs.k8s.io/cluster-api-provider-vsphere/apis/vmware/v1beta1"
clusterapiv1beta1 "sigs.k8s.io/cluster-api/api/v1beta1"
clusterapiutil "sigs.k8s.io/cluster-api/util"
clusterapipatchutil "sigs.k8s.io/cluster-api/util/patch"
Expand All @@ -24,6 +28,8 @@ import (
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/source"

nsxoperatorapi "github.com/vmware-tanzu/nsx-operator/pkg/apis/v1alpha1"

cutil "github.com/vmware-tanzu/tanzu-framework/addons/controllers/utils"
addonconfig "github.com/vmware-tanzu/tanzu-framework/addons/pkg/config"
"github.com/vmware-tanzu/tanzu-framework/addons/pkg/constants"
Expand All @@ -32,6 +38,24 @@ import (
cniv1alpha1 "github.com/vmware-tanzu/tanzu-framework/apis/addonconfigs/cni/v1alpha1"
)

const (
antreaTargetNameSpace = "vmware-system-antrea"
antreaSecretName = "supervisor-cred"
nsxServiceAccountAPIGroup = "nsx.vmware.com"
nsxServiceAccountKind = "nsxserviceaccounts"
clusterNameLabel = "tkg.tanzu.vmware.com/cluster-name"
)

// vsphereAntreaConfigProviderServiceAccountAggregatedClusterRole is the cluster role to assign permissions to capv provider
var vsphereAntreaConfigProviderServiceAccountAggregatedClusterRole = &rbacv1.ClusterRole{
ObjectMeta: metav1.ObjectMeta{
Name: constants.VsphereAntreaConfigProviderServiceAccountAggregatedClusterRole,
Labels: map[string]string{
constants.CAPVClusterRoleAggregationRuleLabelSelectorKey: constants.CAPVClusterRoleAggregationRuleLabelSelectorValue,
},
},
}

// AntreaConfigReconciler reconciles a AntreaConfig object
type AntreaConfigReconciler struct {
client.Client
Expand All @@ -42,6 +66,8 @@ type AntreaConfigReconciler struct {

// +kubebuilder:rbac:groups=addons.tanzu.vmware.com,resources=antreaconfigs,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=addons.tanzu.vmware.com,resources=antreaconfigs/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=vmware.infrastructure.cluster.x-k8s.io,resources=providerserviceaccounts,verbs=get;create;list;watch;update;patch;delete
// +kubebuilder:rbac:groups=nsx.vmware.com,resources=nsxserviceaccounts,verbs=get;create;list;watch;update;patch;delete

// Reconcile is part of the main kubernetes reconciliation loop which aims to
// move the current state of the cluster closer to the desired state.
Expand Down Expand Up @@ -130,6 +156,7 @@ func (r *AntreaConfigReconciler) ReconcileAntreaConfig(

// If AntreaConfig is marked for deletion, then no reconciliation is needed
if !antreaConfig.GetDeletionTimestamp().IsZero() {
r.deregisterAntreaNSX(ctx, antreaConfig, cluster)
return ctrl.Result{}, nil
}

Expand Down Expand Up @@ -167,10 +194,191 @@ func (r *AntreaConfigReconciler) ReconcileAntreaConfigNormal(
return err

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The implementation of ReconcileAntreaConfigDataValue should also be modified to pass Antrea-NSX configs to values.yaml like below:

antrea:
  # … antrea related configs
antrea-nsx:
  enable: true    # set the values according to AntreaConfig.spec.antreaNsx.enable
  bootstrapFrom:  # bootstrapFrom either providerRef or inline configs
    providerRef:    # set the values when Antrea-NSX is bootstrap from NSXServiceAccount. Fill the NSXServiceAccount name created by addon controller to below resource name.
      apiVersion: nsx.vmware.com/v1alpha1
      kind: NSXServiceAccount
      name: test-cluster1-antrea
    inline:  # set the values according to AntreaConfig.spec.antreaNsx.bootstrapFrom.inline is set
      nsxManagers: [sampleNSXIP1, sampleNSXIP2, sampleNSXIP3]
      clusterName: test-cluster1
      nsxCert:
        tls.crt: … # base64 encoded certificate
        tls.key: … # base64 encoded certificate
  config:
    infraType: vSphere # or VMC, AWS, Azure
    # … other configs such as log verbosity, security policy reconciliation timeout

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Antrea Package needs to be updated to support the new fields in values.yaml, too. I'm now sure if existing Antrea Package can ignore these new fields. We can have a try on manually editing the values.yaml in workload cluster and see if Antrea PackageInstall reconciles successfully.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

}

if antreaConfig.Spec.AntreaNsx.BootstrapFrom.ProviderRef != nil && antreaConfig.Spec.AntreaNsx.BootstrapFrom.Inline != nil {
err := fmt.Errorf("providerRef and inline should not be both set in AntreaConfig.spec.antreaNsx.bootstrapFrom")
antreaConfig.Status.Message = err.Error()
} else {
// clear the message here.
antreaConfig.Status.Message = ""
}
// update status.secretRef
dataValueSecretName := util.GenerateDataValueSecretName(cluster.Name, constants.AntreaAddonName)
antreaConfig.Status.SecretRef = dataValueSecretName

return r.registerAntreaNSX(ctx, antreaConfig, cluster)
}

func getClusterName(antreaConfig *cniv1alpha1.AntreaConfig) (name string, exists bool) {
name, exists = antreaConfig.Labels[clusterNameLabel]
if !exists {
index := strings.Index(antreaConfig.Name, "-antrea-package")
if index > 0 {
name = antreaConfig.Name[:index]
exists = true
}
}
return
}

func (r *AntreaConfigReconciler) getProviderServiceAccountName(clusterName string) string {
return fmt.Sprintf("%s-antrea", clusterName)
}

func (r *AntreaConfigReconciler) getNSXServiceAccountName(clusterName string) string {
return fmt.Sprintf("%s-antrea", clusterName)
}

func (r *AntreaConfigReconciler) ensureNsxServiceAccount(ctx context.Context, antreaConfig *cniv1alpha1.AntreaConfig, cluster *clusterapiv1beta1.Cluster) error {
account := &nsxoperatorapi.NSXServiceAccount{}

account.Name = r.getNSXServiceAccountName(cluster.Name)
account.Namespace = antreaConfig.Namespace
account.OwnerReferences = []metav1.OwnerReference{
{
APIVersion: cluster.APIVersion,
Kind: cluster.Kind,
Name: cluster.Name,
UID: cluster.UID,
},
}

err := r.Client.Get(ctx, types.NamespacedName{
Namespace: account.Namespace,
Name: account.Name,
}, account)
if err == nil {
r.Log.Info("NSXServiceAccount %s/%s already exists", account.Namespace, account.Name)
return nil
}
if err != nil && !apierrors.IsNotFound(err) {
r.Log.Info("Found no existing NSXServiceAccount %s/%s", account.Namespace, account.Name)
return err
}

result, err := controllerutil.CreateOrPatch(ctx, r.Client, account, nil)
if err != nil {
r.Log.Error(err, "Error creating or patching NSXServiceAccount", account.Namespace, account.Name)
} else {
r.Log.Info(fmt.Sprintf("NSXServiceAccount %s/%s created %s", account.Namespace, account.Name, result))
}
return err
}

func (r *AntreaConfigReconciler) ensureProviderServiceAccount(ctx context.Context, antreaConfig *cniv1alpha1.AntreaConfig, cluster *clusterapiv1beta1.Cluster) error {
provider := &vsphere.ProviderServiceAccount{}
vsphereCluster, err := cutil.VSphereClusterParavirtualForCAPICluster(ctx, r.Client, cluster)
if err != nil {
return err
}
clusterName, _ := getClusterName(antreaConfig)
nsxSAName := clusterName + "-antrea"
nsxSecretName := clusterName + "-antrea-nsx-cert"
clusterName = vsphereCluster.Name
providerServiceAccountRBACRules := []rbacv1.PolicyRule{

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to our offline discussion, we add -antrea to the resource names.
So the here it should be changed to:

nsxSAName := clusterName+"-antrea"
nsxSecretName := clusterName+"-antrea-nsx-cert"
providerServiceAccountRBACRules := []rbacv1.PolicyRule{
  {..., ResourceNames: []string{nsxSAName}, ...},
  {..., ResourceNames: []string{nsxSecretName }, ...},
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

{
APIGroups: []string{nsxServiceAccountAPIGroup},
Resources: []string{nsxServiceAccountKind},
ResourceNames: []string{nsxSAName},
Verbs: []string{"get", "list", "watch"},
},
{
APIGroups: []string{""},
Resources: []string{"secrets"},
ResourceNames: []string{fmt.Sprintf(nsxSecretName)},
Verbs: []string{"get", "list", "watch"},
},
}
_, err = controllerutil.CreateOrPatch(ctx, r.Client, vsphereAntreaConfigProviderServiceAccountAggregatedClusterRole, func() error {
vsphereAntreaConfigProviderServiceAccountAggregatedClusterRole.Rules = providerServiceAccountRBACRules
return nil
})
if err != nil {
r.Log.Error(err, "Error creating or patching cluster role", "name", vsphereAntreaConfigProviderServiceAccountAggregatedClusterRole)
return err
}
provider.Name = r.getProviderServiceAccountName(clusterName)
provider.Namespace = antreaConfig.Namespace
provider.Spec = vsphere.ProviderServiceAccountSpec{
Ref: &corev1.ObjectReference{
APIVersion: cluster.APIVersion,
Kind: cluster.Kind,
Name: clusterName,
UID: cluster.UID,
},
TargetNamespace: antreaTargetNameSpace,
TargetSecretName: antreaSecretName,
Rules: providerServiceAccountRBACRules,
}
result, err := controllerutil.CreateOrPatch(ctx, r.Client, provider, func() error {
return controllerutil.SetControllerReference(vsphereCluster, provider, r.Scheme)
})
if err != nil {
r.Log.Error(err, "Error creating or patching ProviderServiceAccount", provider.Namespace, provider.Name)
} else {
r.Log.Info(fmt.Sprintf("ProviderServiceAccount %s/%s created %s: %+v", provider.Namespace, provider.Name, result, provider))
}
return err
}

func (r *AntreaConfigReconciler) registerAntreaNSX(ctx context.Context, antreaConfig *cniv1alpha1.AntreaConfig, cluster *clusterapiv1beta1.Cluster) error {
if !antreaConfig.Spec.AntreaNsx.Enable || antreaConfig.Spec.AntreaNsx.BootstrapFrom.Inline != nil {
r.Log.Info("antreaNsx is not enabled or inline is set, there is no ProviderServiceAccount or NsxServiceAccount to be created")
r.deregisterAntreaNSX(ctx, antreaConfig, cluster)
return nil
}
if antreaConfig.Spec.AntreaNsx.BootstrapFrom.ProviderRef != nil {
if strings.ToLower(antreaConfig.Spec.AntreaNsx.BootstrapFrom.ProviderRef.Kind) != nsxServiceAccountKind ||
strings.ToLower(antreaConfig.Spec.AntreaNsx.BootstrapFrom.ProviderRef.ApiGroup) != nsxServiceAccountAPIGroup {
err := fmt.Errorf("either ProviderRef.Kind(%s) or ProviderRef.ApiGroup(%s) is invalid, expcted:ProviderRef.Kind(%s) ProviderRef.ApiGroup(%s)",
antreaConfig.Spec.AntreaNsx.BootstrapFrom.ProviderRef.Kind, antreaConfig.Spec.AntreaNsx.BootstrapFrom.ProviderRef.ApiGroup,
nsxServiceAccountKind, nsxServiceAccountAPIGroup)
antreaConfig.Status.Message = err.Error()
return err
}
}
antreaConfig.Status.Message = ""
err := r.ensureProviderServiceAccount(ctx, antreaConfig, cluster)
if err != nil {
return err
}
err = r.ensureNsxServiceAccount(ctx, antreaConfig, cluster)
return err
}

func (r *AntreaConfigReconciler) deregisterAntreaNSX(ctx context.Context, antreaConfig *cniv1alpha1.AntreaConfig, cluster *clusterapiv1beta1.Cluster) error {
if !antreaConfig.Spec.AntreaNsx.Enable {
r.Log.Info("antreaNsx is not enabled, there is no ProviderServiceAccount or NsxServiceAccount to be deleted")
return nil
}
vsphereCluster, err := cutil.VSphereClusterParavirtualForCAPICluster(ctx, r.Client, cluster)
if err != nil {
if apierrors.IsNotFound(err) {
return nil
}
return err
}
clusterName, exists := getClusterName(antreaConfig)
if !exists {
return fmt.Errorf("invalid antreaConfig Name")
}
account := &nsxoperatorapi.NSXServiceAccount{}

account.Name = r.getNSXServiceAccountName(clusterName)
account.Namespace = antreaConfig.Namespace
err = r.Client.Delete(ctx, account)
if err != nil && !apierrors.IsNotFound(err) {
r.Log.Error(err, "failed to delete NSXServiceAccount", account.Namespace, account.Name)
return err
}

provider := &vsphere.ProviderServiceAccount{}
provider.Name = r.getProviderServiceAccountName(vsphereCluster.Name)
provider.Namespace = vsphereCluster.Namespace
err = r.Client.Delete(ctx, provider)
if err != nil && !apierrors.IsNotFound(err) {
r.Log.Error(err, "failed to delete ProviderServiceAccount", provider.Namespace, provider.Name)
return err
}
return nil
}

Expand Down Expand Up @@ -200,7 +408,7 @@ func (r *AntreaConfigReconciler) ReconcileAntreaConfigDataValue(
antreaDataValuesSecret.StringData = make(map[string]string)

// marshall the yaml contents
antreaConfigYaml, err := mapAntreaConfigSpec(cluster, antreaConfig)
antreaConfigYaml, err := mapAntreaConfigSpec(cluster, antreaConfig, r.Client)
if err != nil {
return err
}
Expand Down
65 changes: 54 additions & 11 deletions addons/controllers/antrea/antreaconfig_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,55 @@ import (
)

// AntreaConfigSpec defines the desired state of AntreaConfig
type antreaConfigSpec struct {
InfraProvider string `yaml:"infraProvider"`
Antrea antrea `yaml:"antrea,omitempty"`
type AntreaConfigSpec struct {
InfraProvider string `yaml:"infraProvider"`
Antrea antrea `yaml:"antrea,omitempty"`
AntreaNsx antreaNsx `yaml:"antreaNsx,omitempty"`
}

type antrea struct {
AntreaConfigDataValue antreaConfigDataValue `yaml:"config,omitempty"`
}

type antreaNsx struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should antrea_configs_exist in clusterbootstrap.yaml be updated to include these ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it should be in another pull request #4098

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry that I misunderstood your comments. because this is not default setting for clusterbootstrap, when user wants to use this feature, they can edit antreaConfig manually. @jeffwubj

Enable bool `yaml:"enable,omitempty"`
BootstrapFrom antreaNsxBootstrapFrom `yaml:"bootstrapFrom,omitempty"`
AntreaNsxConfig antreaNsxConfig `yaml:"config,omitempty"`
}

type antreaNsxBootstrapFrom struct {
// ProviderRef is used with uTKG, which will be filled by NCP operator
ProviderRef *antreaNsxProvider `yaml:"providerRef,omitempty"`
// Inline is used with TKGm, user need to fill in manually
Inline *antreaNsxInline `yaml:"inline,omitempty"`
}

type antreaNsxProvider struct {
// Api version for nsxServiceAccount, its value is "nsx.vmware.com/v1alpha1" now
ApiVersion string `yaml:"apiVersion,omitempty"`
// Its value is NsxServiceAccount
Kind string `yaml:"kind,omitempty"`
// Name is the name for NsxServiceAccount
Name string `yaml:"name,omitempty"`
}

type nsxCertRef struct {
// TLSCert is cert file to access nsx manager
TLSCert string `yaml:"tls.crt,omitempty"`
// TLSKey is key file to access nsx manager
TLSKey string `yaml:"tls.key,omitempty"`
}

type antreaNsxInline struct {
NsxManagers []string `yaml:"nsxManagers,omitempty"`
ClusterName string `yaml:"clusterName,omitempty"`
NsxCertRef nsxCertRef `yaml:"NsxCert,omitempty"`

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's better to put the cert and key in a Secret, so it's flexible to set different RBAC rules for AntreaConfig and Secret. For example, we can have a role which can view and create AntreaConfig, but the role cannot read the Secret.

So here the NsxCertRef can be just a string, the value is the name of the Secret. We can rename this field to NSXSecretName.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

}

type antreaNsxConfig struct {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find that this type doesn't agree with AntreaNsxConfig in antreaconfig_types.go.

type AntreaNsxConfig struct {
	// infraType is the type for infrastructure, so far it is vSphere, VMC, AWS, Azure
	InfraType string `json:"infraType,omitempty"`
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

InfraType string `yaml:"infraType,omitempty"`
}

type antreaEgress struct {
EgressExceptCIDRs []string `yaml:"exceptCIDRs,omitempty"`
}
Expand Down Expand Up @@ -73,9 +113,9 @@ type antreaConfigDataValue struct {
Multicast antreaMulticast `yaml:"multicast,omitempty"`
MultiCluster antreaMultiCluster `yaml:"multicluster,omitempty"`
KubeAPIServerOverride string `yaml:"kubeAPIServerOverride,omitempty"`
transportInterface string `yaml:"transportInterface,omitempty"`
transportInterfaceCIDRs []string `yaml:"transportInterfaceCIDRs,omitempty"`
multicastInterfaces []string `yaml:"multicastInterfaces,omitempty"`
TransportInterface string `yaml:"transportInterface,omitempty"`
TransportInterfaceCIDRs []string `yaml:"transportInterfaceCIDRs,omitempty"`
MulticastInterfaces []string `yaml:"multicastInterfaces,omitempty"`
TunnelType string `yaml:"tunnelType,omitempty"`
TrafficEncryptionMode string `yaml:"trafficEncryptionMode,omitempty"`
EnableUsageReporting bool `yaml:"enableUsageReporting,omitempty"`
Expand Down Expand Up @@ -161,8 +201,8 @@ func (r *AntreaConfigReconciler) ClusterToAntreaConfig(o client.Object) []ctrl.R
return requests
}

func mapAntreaConfigSpec(cluster *clusterv1beta1.Cluster, config *cniv1alpha1.AntreaConfig) (*antreaConfigSpec, error) {
configSpec := &antreaConfigSpec{}
func mapAntreaConfigSpec(cluster *clusterv1beta1.Cluster, config *cniv1alpha1.AntreaConfig, client client.Client) (*AntreaConfigSpec, error) {
configSpec := &AntreaConfigSpec{}

// Derive InfraProvider from the cluster
infraProvider, err := util.GetInfraProvider(cluster)
Expand Down Expand Up @@ -197,9 +237,9 @@ func mapAntreaConfigSpec(cluster *clusterv1beta1.Cluster, config *cniv1alpha1.An
configSpec.Antrea.AntreaConfigDataValue.MultiCluster.Enable = config.Spec.Antrea.AntreaConfigDataValue.MultiCluster.Enable
configSpec.Antrea.AntreaConfigDataValue.MultiCluster.Namespace = config.Spec.Antrea.AntreaConfigDataValue.MultiCluster.Namespace
configSpec.Antrea.AntreaConfigDataValue.KubeAPIServerOverride = config.Spec.Antrea.AntreaConfigDataValue.KubeAPIServerOverride
configSpec.Antrea.AntreaConfigDataValue.transportInterface = config.Spec.Antrea.AntreaConfigDataValue.TransportInterface
configSpec.Antrea.AntreaConfigDataValue.transportInterfaceCIDRs = config.Spec.Antrea.AntreaConfigDataValue.TransportInterfaceCIDRs
configSpec.Antrea.AntreaConfigDataValue.multicastInterfaces = config.Spec.Antrea.AntreaConfigDataValue.MulticastInterfaces
configSpec.Antrea.AntreaConfigDataValue.TransportInterface = config.Spec.Antrea.AntreaConfigDataValue.TransportInterface
configSpec.Antrea.AntreaConfigDataValue.TransportInterfaceCIDRs = config.Spec.Antrea.AntreaConfigDataValue.TransportInterfaceCIDRs
configSpec.Antrea.AntreaConfigDataValue.MulticastInterfaces = config.Spec.Antrea.AntreaConfigDataValue.MulticastInterfaces
configSpec.Antrea.AntreaConfigDataValue.TunnelType = config.Spec.Antrea.AntreaConfigDataValue.TunnelType
configSpec.Antrea.AntreaConfigDataValue.EnableUsageReporting = config.Spec.Antrea.AntreaConfigDataValue.EnableUsageReporting
configSpec.Antrea.AntreaConfigDataValue.WireGuard.Port = config.Spec.Antrea.AntreaConfigDataValue.WireGuard.Port
Expand Down Expand Up @@ -227,5 +267,8 @@ func mapAntreaConfigSpec(cluster *clusterv1beta1.Cluster, config *cniv1alpha1.An
configSpec.Antrea.AntreaConfigDataValue.FeatureGates.MultiCluster = config.Spec.Antrea.AntreaConfigDataValue.FeatureGates.MultiCluster
configSpec.Antrea.AntreaConfigDataValue.FeatureGates.SecondaryNetwork = config.Spec.Antrea.AntreaConfigDataValue.FeatureGates.SecondaryNetwork
configSpec.Antrea.AntreaConfigDataValue.FeatureGates.TrafficControl = config.Spec.Antrea.AntreaConfigDataValue.FeatureGates.TrafficControl

//todo add nsx config here once antreaNsx ia packaged into antrea

return configSpec, nil
}
Loading