Skip to content

Commit

Permalink
Optimize package organization to reduce antctl binary size
Browse files Browse the repository at this point in the history
As a client, antctl needs to know some well-known resource names and
the structs of the API responses. However, the code placement makes
antctl import a lot of packages which are supposed to be consumed by
server side code, e.g.  antrea-controller, antrea-agent.

This patch refactors relevant packages, extracts well-known resources
names and API structs to separate common packages, mostly apis packages.
It reduces antctl binary size by 10MB, from 55MB to 45MB. The placement
also makes it more explicit that these constants and structs are API
related and should be changed more carefully.

The major changes are as follows:
1. Move well-known CRD resource names to types.go files which declare
   these CRD structs.
2. Move other well-known resource names to package pkg/apis.
3. Move structs of API responses to package apis of components which
   provide the corresponding APIs.

Signed-off-by: Quan Tian <qtian@vmware.com>
  • Loading branch information
tnqn committed Mar 26, 2024
1 parent 91f374b commit e7f99b4
Show file tree
Hide file tree
Showing 81 changed files with 840 additions and 696 deletions.
2 changes: 2 additions & 0 deletions cmd/antrea-agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ import (
"antrea.io/antrea/pkg/agent/stats"
support "antrea.io/antrea/pkg/agent/supportbundlecollection"
agenttypes "antrea.io/antrea/pkg/agent/types"
"antrea.io/antrea/pkg/apis"
"antrea.io/antrea/pkg/apis/controlplane"
crdinformers "antrea.io/antrea/pkg/client/informers/externalversions"
crdv1alpha1informers "antrea.io/antrea/pkg/client/informers/externalversions/crd/v1alpha1"
Expand Down Expand Up @@ -893,6 +894,7 @@ func run(o *Options) error {
authorization,
*o.config.EnablePrometheusMetrics,
o.config.ClientConnection.Kubeconfig,
apis.APIServerLoopbackTokenPath,
v4Enabled,
v6Enabled)
if err != nil {
Expand Down
8 changes: 4 additions & 4 deletions cmd/antrea-controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ import (
policyv1a1informers "sigs.k8s.io/network-policy-api/pkg/client/informers/externalversions"

mcinformers "antrea.io/antrea/multicluster/pkg/client/informers/externalversions"
antreaapis "antrea.io/antrea/pkg/apis"
"antrea.io/antrea/pkg/apis"
"antrea.io/antrea/pkg/apiserver"
"antrea.io/antrea/pkg/apiserver/certificate"
"antrea.io/antrea/pkg/apiserver/openapi"
Expand Down Expand Up @@ -238,7 +238,7 @@ func run(o *Options) error {
var csrLister csrlisters.CertificateSigningRequestLister
if features.DefaultFeatureGate.Enabled(features.IPsecCertAuth) {
csrInformer = csrinformers.NewFilteredCertificateSigningRequestInformer(client, 0, nil, func(listOptions *metav1.ListOptions) {
listOptions.FieldSelector = fields.OneTermEqualSelector("spec.signerName", antreaapis.AntreaIPsecCSRSignerName).String()
listOptions.FieldSelector = fields.OneTermEqualSelector("spec.signerName", apis.AntreaIPsecCSRSignerName).String()
})
csrLister = csrlisters.NewCertificateSigningRequestLister(csrInformer.GetIndexer())

Expand Down Expand Up @@ -533,10 +533,10 @@ func createAPIServerConfig(kubeconfig string,
return nil, err
}

if err := os.MkdirAll(path.Dir(apiserver.TokenPath), os.ModeDir); err != nil {
if err := os.MkdirAll(path.Dir(apis.APIServerLoopbackTokenPath), os.ModeDir); err != nil {
return nil, fmt.Errorf("error when creating dirs of token file: %v", err)
}
if err := os.WriteFile(apiserver.TokenPath, []byte(serverConfig.LoopbackClientConfig.BearerToken), 0600); err != nil {
if err := os.WriteFile(apis.APIServerLoopbackTokenPath, []byte(serverConfig.LoopbackClientConfig.BearerToken), 0600); err != nil {
return nil, fmt.Errorf("error when writing loopback access token to file: %v", err)
}
serverConfig.OpenAPIConfig = genericapiserver.DefaultOpenAPIConfig(
Expand Down
16 changes: 16 additions & 0 deletions pkg/agent/apis/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright 2024 Antrea Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package apis contains API definitions used to interface with antrea-agent.
package apis
192 changes: 192 additions & 0 deletions pkg/agent/apis/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
// Copyright 2024 Antrea Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package apis

import (
"strconv"
"strings"

corev1 "k8s.io/api/core/v1"

"antrea.io/antrea/pkg/apis/crd/v1beta1"
"antrea.io/antrea/pkg/util/printers"
)

// AntreaAgentInfoResponse is the struct for the response of agentinfo command.
// It includes all fields except meta info from v1beta1.AntreaAgentInfo struct.
type AntreaAgentInfoResponse struct {
Version string `json:"version,omitempty"` // Antrea binary version
PodRef corev1.ObjectReference `json:"podRef,omitempty"` // The Pod that Antrea Agent is running in
NodeRef corev1.ObjectReference `json:"nodeRef,omitempty"` // The Node that Antrea Agent is running in
NodeSubnets []string `json:"nodeSubnets,omitempty"` // Node subnets
OVSInfo v1beta1.OVSInfo `json:"ovsInfo,omitempty"` // OVS Information
NetworkPolicyControllerInfo v1beta1.NetworkPolicyControllerInfo `json:"networkPolicyControllerInfo,omitempty"` // Antrea Agent NetworkPolicy information
LocalPodNum int32 `json:"localPodNum,omitempty"` // The number of Pods which the agent is in charge of
AgentConditions []v1beta1.AgentCondition `json:"agentConditions,omitempty"` // Agent condition contains types like AgentHealthy
}

func (r AntreaAgentInfoResponse) GetTableHeader() []string {
return []string{"POD", "NODE", "STATUS", "NODE-SUBNET", "NETWORK-POLICIES", "ADDRESS-GROUPS", "APPLIED-TO-GROUPS", "LOCAL-PODS"}
}

func (r AntreaAgentInfoResponse) getAgentConditionStr() string {
if r.AgentConditions == nil {
return ""
}
agentCondition := "Healthy"
for _, cond := range r.AgentConditions {
if cond.Status == corev1.ConditionUnknown {
agentCondition = "Unknown"
}
if cond.Status == corev1.ConditionFalse {
return "Unhealthy"
}
}
return agentCondition
}

func (r AntreaAgentInfoResponse) GetTableRow(maxColumnLength int) []string {
return []string{r.PodRef.Namespace + "/" + r.PodRef.Name,
r.NodeRef.Name,
r.getAgentConditionStr(),
printers.GenerateTableElementWithSummary(r.NodeSubnets, maxColumnLength),
strconv.Itoa(int(r.NetworkPolicyControllerInfo.NetworkPolicyNum)),
strconv.Itoa(int(r.NetworkPolicyControllerInfo.AddressGroupNum)),
strconv.Itoa(int(r.NetworkPolicyControllerInfo.AppliedToGroupNum)),
strconv.Itoa(int(r.LocalPodNum))}
}

func (r AntreaAgentInfoResponse) SortRows() bool {
return true
}

type FeatureGateResponse struct {
Component string `json:"component,omitempty"`
Name string `json:"name,omitempty"`
Status string `json:"status,omitempty"`
Version string `json:"version,omitempty"`
}

// MemberlistResponse describes the response struct of memberlist command.
type MemberlistResponse struct {
NodeName string `json:"nodeName,omitempty"`
IP string `json:"ip,omitempty"`
Status string `json:"status,omitempty"`
}

func (r MemberlistResponse) GetTableHeader() []string {
return []string{"NODE", "IP", "STATUS"}
}

func (r MemberlistResponse) GetTableRow(_ int) []string {
return []string{r.NodeName, r.IP, r.Status}
}

func (r MemberlistResponse) SortRows() bool {
return true
}

type MulticastResponse struct {
PodName string `json:"name,omitempty" antctl:"name,Name of the Pod"`
PodNamespace string `json:"podNamespace,omitempty"`
Inbound string `json:"inbound,omitempty"`
Outbound string `json:"outbound,omitempty"`
}

func (r MulticastResponse) GetTableHeader() []string {
return []string{"NAMESPACE", "NAME", "INBOUND", "OUTBOUND"}
}

func (r MulticastResponse) GetTableRow(_ int) []string {
return []string{r.PodNamespace, r.PodName, r.Inbound, r.Outbound}
}

func (r MulticastResponse) SortRows() bool {
return true
}

// OVSFlowResponse is the response struct of ovsflows command.
type OVSFlowResponse struct {
Flow string `json:"flow,omitempty"`
}

func (r OVSFlowResponse) GetTableHeader() []string {
return []string{""}
}

func (r OVSFlowResponse) GetTableRow(maxColumnLength int) []string {
return []string{r.Flow}
}

func (r OVSFlowResponse) SortRows() bool {
return false
}

// OVSTracingResponse is the response struct of ovstracing command.
type OVSTracingResponse struct {
Result string `json:"result,omitempty"`
}

// PodInterfaceResponse describes the response struct of pod-interface command.
type PodInterfaceResponse struct {
PodName string `json:"name,omitempty" antctl:"name,Name of the Pod"`
PodNamespace string `json:"podNamespace,omitempty"`
InterfaceName string `json:"interfaceName,omitempty"`
IPs []string `json:"ips,omitempty"`
MAC string `json:"mac,omitempty"`
PortUUID string `json:"portUUID,omitempty"`
OFPort int32 `json:"ofPort,omitempty"`
ContainerID string `json:"containerID,omitempty"`
}

func (r PodInterfaceResponse) GetTableHeader() []string {
return []string{"NAMESPACE", "NAME", "INTERFACE-NAME", "IP", "MAC", "PORT-UUID", "OF-PORT", "CONTAINER-ID"}
}

func (r PodInterfaceResponse) getContainerIDStr() string {
if len(r.ContainerID) > 12 {
return r.ContainerID[0:11]
}
return r.ContainerID
}

func (r PodInterfaceResponse) GetTableRow(_ int) []string {
return []string{r.PodNamespace, r.PodName, r.InterfaceName, strings.Join(r.IPs, ", "), r.MAC, r.PortUUID, strconv.Itoa(int(r.OFPort)), r.getContainerIDStr()}
}

func (r PodInterfaceResponse) SortRows() bool {
return true
}

// ServiceExternalIPInfo contains the essential information for Services with type of Loadbalancer managed by Antrea.
type ServiceExternalIPInfo struct {
ServiceName string `json:"serviceName,omitempty" antctl:"name,Name of the Service"`
Namespace string `json:"namespace,omitempty"`
ExternalIP string `json:"externalIP,omitempty"`
ExternalIPPool string `json:"externalIPPool,omitempty"`
AssignedNode string `json:"assignedNode,omitempty"`
}

func (r ServiceExternalIPInfo) GetTableHeader() []string {
return []string{"NAMESPACE", "NAME", "EXTERNAL-IP-POOL", "EXTERNAL-IP", "ASSIGNED-NODE"}
}

func (r ServiceExternalIPInfo) GetTableRow(_ int) []string {
return []string{r.Namespace, r.ServiceName, r.ExternalIPPool, r.ExternalIP, r.AssignedNode}
}

func (r ServiceExternalIPInfo) SortRows() bool {
return true
}
10 changes: 5 additions & 5 deletions pkg/agent/apiserver/apiserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,6 @@ const CertPairName = "antrea-agent-api"
var (
scheme = runtime.NewScheme()
codecs = serializer.NewCodecFactory(scheme)
// #nosec G101: false positive triggered by variable name which includes "token"
TokenPath = "/var/run/antrea/apiserver/loopback-client-token"
)

func init() {
Expand Down Expand Up @@ -121,10 +119,11 @@ func New(aq agentquerier.AgentQuerier,
authorization *genericoptions.DelegatingAuthorizationOptions,
enableMetrics bool,
kubeconfig string,
loopbackClientTokenPath string,
v4Enabled,
v6Enabled bool,
) (*agentAPIServer, error) {
cfg, err := newConfig(aq, npq, secureServing, authentication, authorization, enableMetrics, kubeconfig)
cfg, err := newConfig(aq, npq, secureServing, authentication, authorization, enableMetrics, kubeconfig, loopbackClientTokenPath)
if err != nil {
return nil, err
}
Expand All @@ -146,6 +145,7 @@ func newConfig(aq agentquerier.AgentQuerier,
authorization *genericoptions.DelegatingAuthorizationOptions,
enableMetrics bool,
kubeconfig string,
loopbackClientTokenPath string,
) (*genericapiserver.CompletedConfig, error) {
// kubeconfig file is useful when antrea-agent isn't running as a Pod.
if len(kubeconfig) > 0 {
Expand All @@ -170,10 +170,10 @@ func newConfig(aq agentquerier.AgentQuerier,
if err := authorization.ApplyTo(&serverConfig.Authorization); err != nil {
return nil, err
}
if err := os.MkdirAll(path.Dir(TokenPath), os.ModeDir); err != nil {
if err := os.MkdirAll(path.Dir(loopbackClientTokenPath), os.ModeDir); err != nil {
return nil, fmt.Errorf("error when creating dirs of token file: %v", err)
}
if err := os.WriteFile(TokenPath, []byte(serverConfig.LoopbackClientConfig.BearerToken), 0600); err != nil {
if err := os.WriteFile(loopbackClientTokenPath, []byte(serverConfig.LoopbackClientConfig.BearerToken), 0600); err != nil {
return nil, fmt.Errorf("error when writing loopback access token to file: %v", err)
}
v := antreaversion.GetVersion()
Expand Down
28 changes: 8 additions & 20 deletions pkg/agent/apiserver/apiserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"sync"
"testing"
"time"
Expand All @@ -44,15 +45,9 @@ type fakeAgentAPIServer struct {
}

func newFakeAPIServer(t *testing.T) *fakeAgentAPIServer {
kubeConfigFile, err := os.CreateTemp("", "kubeconfig")
if err != nil {
t.Fatal(err)
}
defer func() {
kubeConfigFile.Close()
os.Remove(kubeConfigFile.Name())
}()
if _, err := kubeConfigFile.Write([]byte(`
tempDir := t.TempDir()
kubeConfigPath := filepath.Join(tempDir, "kubeconfig")
kubeConfigContent := `
apiVersion: v1
kind: Config
clusters:
Expand All @@ -64,18 +59,11 @@ contexts:
cluster: cluster
name: cluster
current-context: cluster
`)); err != nil {
`
if err := os.WriteFile(kubeConfigPath, []byte(kubeConfigContent), 0600); err != nil {
t.Fatal(err)
}
originalTokenPath := TokenPath
tokenFile, err := os.CreateTemp("", "token")
require.NoError(t, err)
TokenPath = tokenFile.Name()
defer func() {
TokenPath = originalTokenPath
tokenFile.Close()
os.Remove(tokenFile.Name())
}()
tokenPath := filepath.Join(tempDir, "token")
version.Version = "v1.2.3"
ctrl := gomock.NewController(t)
agentQuerier := aqtest.NewMockAgentQuerier(ctrl)
Expand All @@ -91,7 +79,7 @@ current-context: cluster
// InClusterLookup is skipped when testing, otherwise it would always fail as there is no real cluster.
authentication.SkipInClusterLookup = true
authorization := options.NewDelegatingAuthorizationOptions().WithAlwaysAllowPaths("/healthz", "/livez", "/readyz")
apiServer, err := New(agentQuerier, npQuerier, nil, nil, secureServing, authentication, authorization, true, kubeConfigFile.Name(), true, true)
apiServer, err := New(agentQuerier, npQuerier, nil, nil, secureServing, authentication, authorization, true, kubeConfigPath, tokenPath, true, true)
require.NoError(t, err)
fakeAPIServer := &fakeAgentAPIServer{
agentAPIServer: apiServer,
Expand Down
Loading

0 comments on commit e7f99b4

Please sign in to comment.