istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pilot/pkg/config/monitor/monitor_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 monitor
    16  
    17  import (
    18  	"errors"
    19  	"testing"
    20  	"time"
    21  
    22  	. "github.com/onsi/gomega"
    23  
    24  	networking "istio.io/api/networking/v1alpha3"
    25  	"istio.io/istio/pilot/pkg/config/memory"
    26  	"istio.io/istio/pkg/config"
    27  	"istio.io/istio/pkg/config/schema/collection"
    28  	"istio.io/istio/pkg/config/schema/collections"
    29  	"istio.io/istio/pkg/config/schema/gvk"
    30  	"istio.io/istio/pkg/test/util/retry"
    31  )
    32  
    33  var createConfigSet = []*config.Config{
    34  	{
    35  		Meta: config.Meta{
    36  			Name:             "magic",
    37  			GroupVersionKind: gvk.Gateway,
    38  		},
    39  		Spec: &networking.Gateway{
    40  			Servers: []*networking.Server{
    41  				{
    42  					Port: &networking.Port{
    43  						Number:   80,
    44  						Protocol: "HTTP",
    45  						Name:     "http",
    46  					},
    47  					Hosts: []string{"*.example.com"},
    48  				},
    49  			},
    50  		},
    51  	},
    52  }
    53  
    54  var updateConfigSet = []*config.Config{
    55  	{
    56  		Meta: config.Meta{
    57  			Name:             "magic",
    58  			GroupVersionKind: gvk.Gateway,
    59  		},
    60  		Spec: &networking.Gateway{
    61  			Servers: []*networking.Server{
    62  				{
    63  					Port: &networking.Port{
    64  						Number:   80,
    65  						Protocol: "HTTP2",
    66  						Name:     "http",
    67  					},
    68  					Hosts: []string{"*.example.com"},
    69  				},
    70  			},
    71  		},
    72  	},
    73  }
    74  
    75  func TestMonitorForChange(t *testing.T) {
    76  	g := NewWithT(t)
    77  
    78  	store := memory.Make(collection.SchemasFor(collections.Gateway))
    79  
    80  	var (
    81  		callCount int
    82  		configs   []*config.Config
    83  		err       error
    84  	)
    85  
    86  	someConfigFunc := func() ([]*config.Config, error) {
    87  		switch callCount {
    88  		case 0:
    89  			configs = createConfigSet
    90  			err = nil
    91  		case 3:
    92  			configs = updateConfigSet
    93  		case 6:
    94  			configs = []*config.Config{}
    95  		}
    96  
    97  		callCount++
    98  		return configs, err
    99  	}
   100  	mon := NewMonitor("", store, someConfigFunc, "")
   101  	stop := make(chan struct{})
   102  	defer func() { close(stop) }()
   103  	mon.Start(stop)
   104  
   105  	go func() {
   106  		for i := 0; i < 10; i++ {
   107  			select {
   108  			case <-stop:
   109  				return
   110  			case mon.updateCh <- struct{}{}:
   111  			}
   112  			time.Sleep(time.Millisecond * 100)
   113  		}
   114  	}()
   115  	g.Eventually(func() error {
   116  		c := store.List(gvk.Gateway, "")
   117  
   118  		if len(c) != 1 {
   119  			return errors.New("no configs")
   120  		}
   121  
   122  		if c[0].Meta.Name != "magic" {
   123  			return errors.New("wrong config")
   124  		}
   125  
   126  		return nil
   127  	}).Should(Succeed())
   128  
   129  	g.Eventually(func() error {
   130  		c := store.List(gvk.Gateway, "")
   131  		if len(c) == 0 {
   132  			return errors.New("no config")
   133  		}
   134  
   135  		gateway := c[0].Spec.(*networking.Gateway)
   136  		if gateway.Servers[0].Port.Protocol != "HTTP2" {
   137  			return errors.New("protocol has not been updated")
   138  		}
   139  
   140  		return nil
   141  	}).Should(Succeed())
   142  
   143  	g.Eventually(func() []config.Config {
   144  		return store.List(gvk.Gateway, "")
   145  	}).Should(HaveLen(0))
   146  }
   147  
   148  func TestMonitorFileSnapshot(t *testing.T) {
   149  	ts := &testState{
   150  		ConfigFiles: map[string][]byte{"gateway.yml": []byte(statusRegressionYAML)},
   151  	}
   152  
   153  	ts.testSetup(t)
   154  
   155  	store := memory.Make(collection.SchemasFor(collections.Gateway))
   156  	fileWatcher := NewFileSnapshot(ts.rootPath, collection.SchemasFor(), "foo")
   157  
   158  	mon := NewMonitor("", store, fileWatcher.ReadConfigFiles, "")
   159  	stop := make(chan struct{})
   160  	defer func() { close(stop) }()
   161  	mon.Start(stop)
   162  	retry.UntilOrFail(t, func() bool { return store.Get(gvk.Gateway, "test", "test-1") != nil })
   163  }
   164  
   165  func TestMonitorForError(t *testing.T) {
   166  	g := NewWithT(t)
   167  
   168  	store := memory.Make(collection.SchemasFor(collections.Gateway))
   169  
   170  	var (
   171  		callCount int
   172  		configs   []*config.Config
   173  		err       error
   174  	)
   175  
   176  	delay := make(chan struct{}, 1)
   177  
   178  	someConfigFunc := func() ([]*config.Config, error) {
   179  		switch callCount {
   180  		case 0:
   181  			configs = createConfigSet
   182  			err = nil
   183  		case 3:
   184  			configs = nil
   185  			err = errors.New("snapshotFunc can't connect")
   186  			delay <- struct{}{}
   187  		}
   188  
   189  		callCount++
   190  		return configs, err
   191  	}
   192  	mon := NewMonitor("", store, someConfigFunc, "")
   193  	stop := make(chan struct{})
   194  	defer func() { close(stop) }()
   195  	mon.Start(stop)
   196  
   197  	go func() {
   198  		updateTicker := time.NewTicker(100 * time.Millisecond)
   199  		numUpdates := 10
   200  		for {
   201  			select {
   202  			case <-stop:
   203  				updateTicker.Stop()
   204  				return
   205  			case <-updateTicker.C:
   206  				mon.updateCh <- struct{}{}
   207  				numUpdates--
   208  				if numUpdates == 0 {
   209  					updateTicker.Stop()
   210  					return
   211  				}
   212  			}
   213  		}
   214  	}()
   215  	// Test ensures that after a coplilot connection error the data remains
   216  	// nil data return and error return keeps the existing data aka createConfigSet
   217  	<-delay
   218  	g.Eventually(func() error {
   219  		c := store.List(gvk.Gateway, "")
   220  
   221  		if len(c) != 1 {
   222  			return errors.New("config files erased on Copilot error")
   223  		}
   224  
   225  		return nil
   226  	}).Should(Succeed())
   227  }