istio.io/istio@v0.0.0-20240520182934-d79c90f27776/istioctl/pkg/writer/pilot/status_test.go (about)

     1  // Copyright Istio Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package pilot
    16  
    17  import (
    18  	"bytes"
    19  	"encoding/json"
    20  	"os"
    21  	"testing"
    22  
    23  	core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
    24  	discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
    25  	status "github.com/envoyproxy/go-control-plane/envoy/service/status/v3"
    26  	"google.golang.org/protobuf/types/known/anypb"
    27  
    28  	"istio.io/istio/pilot/pkg/model"
    29  	"istio.io/istio/pilot/pkg/util/protoconv"
    30  	"istio.io/istio/pilot/pkg/xds"
    31  	v3 "istio.io/istio/pilot/pkg/xds/v3"
    32  	"istio.io/istio/pkg/cluster"
    33  	"istio.io/istio/pkg/test/util/assert"
    34  	istioversion "istio.io/istio/pkg/version"
    35  	"istio.io/istio/tests/util"
    36  )
    37  
    38  func TestXdsStatusWriter_PrintAll(t *testing.T) {
    39  	tests := []struct {
    40  		name    string
    41  		input   map[string]*discovery.DiscoveryResponse
    42  		want    string
    43  		wantErr bool
    44  	}{
    45  		{
    46  			name: "prints multiple istiod inputs to buffer in alphabetical order by pod name",
    47  			input: map[string]*discovery.DiscoveryResponse{
    48  				"istiod1": xdsResponseInput("istiod1", []clientConfigInput{
    49  					{
    50  						proxyID:        "proxy1",
    51  						clusterID:      "cluster1",
    52  						version:        "1.20",
    53  						cdsSyncStatus:  status.ConfigStatus_STALE,
    54  						ldsSyncStatus:  status.ConfigStatus_SYNCED,
    55  						rdsSyncStatus:  status.ConfigStatus_NOT_SENT,
    56  						edsSyncStatus:  status.ConfigStatus_SYNCED,
    57  						ecdsSyncStatus: status.ConfigStatus_SYNCED,
    58  					},
    59  				}),
    60  				"istiod2": xdsResponseInput("istiod2", []clientConfigInput{
    61  					{
    62  						proxyID:        "proxy2",
    63  						clusterID:      "cluster2",
    64  						version:        "1.19",
    65  						cdsSyncStatus:  status.ConfigStatus_STALE,
    66  						ldsSyncStatus:  status.ConfigStatus_SYNCED,
    67  						rdsSyncStatus:  status.ConfigStatus_SYNCED,
    68  						edsSyncStatus:  status.ConfigStatus_STALE,
    69  						ecdsSyncStatus: status.ConfigStatus_STALE,
    70  					},
    71  				}),
    72  				"istiod3": xdsResponseInput("istiod3", []clientConfigInput{
    73  					{
    74  						proxyID:        "proxy3",
    75  						clusterID:      "cluster3",
    76  						version:        "1.20",
    77  						cdsSyncStatus:  status.ConfigStatus_NOT_SENT,
    78  						ldsSyncStatus:  status.ConfigStatus_ERROR,
    79  						rdsSyncStatus:  status.ConfigStatus_NOT_SENT,
    80  						edsSyncStatus:  status.ConfigStatus_STALE,
    81  						ecdsSyncStatus: status.ConfigStatus_NOT_SENT,
    82  					},
    83  				}),
    84  				"istiod4": xdsResponseInput("istiod4", []clientConfigInput{
    85  					{
    86  						proxyID:        "proxy4",
    87  						clusterID:      "cluster4",
    88  						version:        "1.20",
    89  						cdsSyncStatus:  status.ConfigStatus_UNKNOWN,
    90  						ldsSyncStatus:  status.ConfigStatus_UNKNOWN,
    91  						rdsSyncStatus:  status.ConfigStatus_UNKNOWN,
    92  						edsSyncStatus:  status.ConfigStatus_UNKNOWN,
    93  						ecdsSyncStatus: status.ConfigStatus_UNKNOWN,
    94  					},
    95  				}),
    96  			},
    97  			want: "testdata/multiXdsStatusMultiPilot.txt",
    98  		},
    99  		{
   100  			name: "prints single istiod input to buffer in alphabetical order by pod name",
   101  			input: map[string]*discovery.DiscoveryResponse{
   102  				"istiod1": xdsResponseInput("istiod1", []clientConfigInput{
   103  					{
   104  						proxyID:        "proxy1",
   105  						clusterID:      "cluster1",
   106  						version:        "1.20",
   107  						cdsSyncStatus:  status.ConfigStatus_STALE,
   108  						ldsSyncStatus:  status.ConfigStatus_SYNCED,
   109  						rdsSyncStatus:  status.ConfigStatus_NOT_SENT,
   110  						edsSyncStatus:  status.ConfigStatus_SYNCED,
   111  						ecdsSyncStatus: status.ConfigStatus_NOT_SENT,
   112  					},
   113  					{
   114  						proxyID:        "proxy2",
   115  						clusterID:      "cluster2",
   116  						version:        "1.20",
   117  						cdsSyncStatus:  status.ConfigStatus_STALE,
   118  						ldsSyncStatus:  status.ConfigStatus_SYNCED,
   119  						rdsSyncStatus:  status.ConfigStatus_SYNCED,
   120  						edsSyncStatus:  status.ConfigStatus_STALE,
   121  						ecdsSyncStatus: status.ConfigStatus_NOT_SENT,
   122  					},
   123  				}),
   124  			},
   125  			want: "testdata/multiXdsStatusSinglePilot.txt",
   126  		},
   127  	}
   128  	for _, tt := range tests {
   129  		t.Run(tt.name, func(t *testing.T) {
   130  			got := &bytes.Buffer{}
   131  			sw := XdsStatusWriter{Writer: got}
   132  			input := map[string]*discovery.DiscoveryResponse{}
   133  			for key, ss := range tt.input {
   134  				input[key] = ss
   135  			}
   136  
   137  			err := sw.PrintAll(input)
   138  			if tt.wantErr {
   139  				assert.Error(t, err)
   140  			} else {
   141  				assert.NoError(t, err)
   142  			}
   143  			want, _ := os.ReadFile(tt.want)
   144  			if err := util.Compare(got.Bytes(), want); err != nil {
   145  				t.Errorf(err.Error())
   146  			}
   147  		})
   148  	}
   149  }
   150  
   151  const clientConfigType = "type.googleapis.com/envoy.service.status.v3.ClientConfig"
   152  
   153  type clientConfigInput struct {
   154  	proxyID   string
   155  	clusterID string
   156  	version   string
   157  
   158  	cdsSyncStatus  status.ConfigStatus
   159  	ldsSyncStatus  status.ConfigStatus
   160  	rdsSyncStatus  status.ConfigStatus
   161  	edsSyncStatus  status.ConfigStatus
   162  	ecdsSyncStatus status.ConfigStatus
   163  }
   164  
   165  func newXdsClientConfig(config clientConfigInput) *status.ClientConfig {
   166  	meta := model.NodeMetadata{
   167  		ClusterID:    cluster.ID(config.clusterID),
   168  		IstioVersion: config.version,
   169  	}
   170  	return &status.ClientConfig{
   171  		Node: &core.Node{
   172  			Id:       config.proxyID,
   173  			Metadata: meta.ToStruct(),
   174  		},
   175  		GenericXdsConfigs: []*status.ClientConfig_GenericXdsConfig{
   176  			{
   177  				TypeUrl:      v3.ClusterType,
   178  				ConfigStatus: config.cdsSyncStatus,
   179  			},
   180  			{
   181  				TypeUrl:      v3.ListenerType,
   182  				ConfigStatus: config.ldsSyncStatus,
   183  			},
   184  			{
   185  				TypeUrl:      v3.RouteType,
   186  				ConfigStatus: config.rdsSyncStatus,
   187  			},
   188  			{
   189  				TypeUrl:      v3.EndpointType,
   190  				ConfigStatus: config.edsSyncStatus,
   191  			},
   192  			{
   193  				TypeUrl:      v3.ExtensionConfigurationType,
   194  				ConfigStatus: config.ecdsSyncStatus,
   195  			},
   196  		},
   197  	}
   198  }
   199  
   200  func xdsResponseInput(istiodID string, configInputs []clientConfigInput) *discovery.DiscoveryResponse {
   201  	icp := &xds.IstioControlPlaneInstance{
   202  		Component: "istiod",
   203  		ID:        istiodID,
   204  		Info: istioversion.BuildInfo{
   205  			Version: "1.1",
   206  		},
   207  	}
   208  	identifier, _ := json.Marshal(icp)
   209  
   210  	resources := make([]*anypb.Any, 0)
   211  	for _, input := range configInputs {
   212  		resources = append(resources, protoconv.MessageToAny(newXdsClientConfig(input)))
   213  	}
   214  
   215  	return &discovery.DiscoveryResponse{
   216  		VersionInfo: "1.1",
   217  		TypeUrl:     clientConfigType,
   218  		Resources:   resources,
   219  		ControlPlane: &core.ControlPlane{
   220  			Identifier: string(identifier),
   221  		},
   222  	}
   223  }