github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/state/machine_ports_ops_test.go (about)

     1  // Copyright 2020 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package state
     5  
     6  import (
     7  	"github.com/juju/collections/set"
     8  	"github.com/juju/testing"
     9  	jc "github.com/juju/testing/checkers"
    10  	gc "gopkg.in/check.v1"
    11  
    12  	"github.com/juju/juju/core/network"
    13  )
    14  
    15  type MachinePortsOpsSuite struct {
    16  	testing.IsolationSuite
    17  }
    18  
    19  var _ = gc.Suite(&MachinePortsOpsSuite{})
    20  
    21  func (MachinePortsOpsSuite) TestPruneOpenPorts(c *gc.C) {
    22  	op := &openClosePortRangesOperation{
    23  		updatedUnitPortRanges: map[string]network.GroupedPortRanges{
    24  			"enigma/0": {
    25  				"": []network.PortRange{
    26  					network.MustParsePortRange("1234-1337/tcp"),
    27  					network.MustParsePortRange("8080/tcp"),
    28  					network.MustParsePortRange("17017/tcp"),
    29  				},
    30  				// The following ranges are also present in the wildcard list.
    31  				// They are therefore redundant and should be pruned.
    32  				"dmz": []network.PortRange{
    33  					network.MustParsePortRange("1234-1337/tcp"),
    34  				},
    35  				"public": []network.PortRange{
    36  					network.MustParsePortRange("8080/tcp"),
    37  				},
    38  			},
    39  		},
    40  	}
    41  
    42  	modified := op.pruneOpenPorts()
    43  	c.Assert(modified, jc.IsTrue, gc.Commentf("expected pruneOpenPorts to modify the port list"))
    44  
    45  	exp := map[string]network.GroupedPortRanges{
    46  		"enigma/0": {
    47  			"": []network.PortRange{
    48  				network.MustParsePortRange("1234-1337/tcp"),
    49  				network.MustParsePortRange("8080/tcp"),
    50  				network.MustParsePortRange("17017/tcp"),
    51  			},
    52  			// Pruned endpoint lists should remain empty
    53  			"dmz":    []network.PortRange{},
    54  			"public": []network.PortRange{},
    55  		},
    56  	}
    57  	c.Assert(op.updatedUnitPortRanges, gc.DeepEquals, exp, gc.Commentf("expected pruneOpenPorts to remove redundant sections for the dmz, public endpoints"))
    58  }
    59  
    60  func (MachinePortsOpsSuite) TestPruneEmptySections(c *gc.C) {
    61  	op := &openClosePortRangesOperation{
    62  		updatedUnitPortRanges: map[string]network.GroupedPortRanges{
    63  			"enigma/0": {
    64  				"": []network.PortRange{
    65  					network.MustParsePortRange("1234-1337/tcp"),
    66  					network.MustParsePortRange("8080/tcp"),
    67  					network.MustParsePortRange("17017/tcp"),
    68  				},
    69  				"dmz":    []network.PortRange{},
    70  				"public": []network.PortRange{},
    71  			},
    72  			// Since all sections are empty, the prune code is expected
    73  			// to remove the entire map entry for enigma/1
    74  			"enigma/1": {
    75  				"":        []network.PortRange{},
    76  				"coffee":  []network.PortRange{},
    77  				"private": []network.PortRange{},
    78  			},
    79  		},
    80  	}
    81  
    82  	modified := op.pruneEmptySections()
    83  	c.Assert(modified, jc.IsTrue, gc.Commentf("expected pruneEmptySections to modify the port list"))
    84  
    85  	exp := map[string]network.GroupedPortRanges{
    86  		"enigma/0": {
    87  			"": []network.PortRange{
    88  				network.MustParsePortRange("1234-1337/tcp"),
    89  				network.MustParsePortRange("8080/tcp"),
    90  				network.MustParsePortRange("17017/tcp"),
    91  			},
    92  		},
    93  	}
    94  	c.Assert(op.updatedUnitPortRanges, gc.DeepEquals, exp, gc.Commentf("expected prineEmptySections to remove all empty sections and unit docs"))
    95  }
    96  
    97  func (MachinePortsOpsSuite) TestMergePendingOpenPortRangesConflict(c *gc.C) {
    98  	op := &openClosePortRangesOperation{
    99  		mpr: &machinePortRanges{
   100  			doc: machinePortRangesDoc{
   101  				UnitRanges: map[string]network.GroupedPortRanges{
   102  					"enigma/0": {
   103  						"": []network.PortRange{
   104  							network.MustParsePortRange("1234-1337/tcp"),
   105  							network.MustParsePortRange("8080/tcp"),
   106  							network.MustParsePortRange("17017/tcp"),
   107  						},
   108  					},
   109  				},
   110  			},
   111  			pendingOpenRanges: map[string]network.GroupedPortRanges{
   112  				"enigma/1": {
   113  					"tea": []network.PortRange{
   114  						network.MustParsePortRange("1242/tcp"),
   115  					},
   116  				},
   117  			},
   118  		},
   119  	}
   120  
   121  	op.cloneExistingUnitPortRanges()
   122  	op.buildPortRangeToUnitMap()
   123  
   124  	_, err := op.mergePendingOpenPortRanges()
   125  	c.Assert(err, gc.ErrorMatches, `.*port ranges 1234-1337/tcp \("enigma/0"\) and 1242/tcp \("enigma/1"\) conflict`)
   126  }
   127  
   128  func (MachinePortsOpsSuite) TestMergePendingOpenPortRangeDupHandling(c *gc.C) {
   129  	specs := []struct {
   130  		descr       string
   131  		existing    map[string]network.GroupedPortRanges
   132  		pendingOpen map[string]network.GroupedPortRanges
   133  		exp         map[string]network.GroupedPortRanges
   134  		expModified bool
   135  	}{
   136  		{
   137  			descr: "port range already opened by the unit for all endpoints",
   138  			existing: map[string]network.GroupedPortRanges{
   139  				"enigma/0": {
   140  					"": []network.PortRange{network.MustParsePortRange("8080/tcp")},
   141  				},
   142  			},
   143  			pendingOpen: map[string]network.GroupedPortRanges{
   144  				"enigma/0": {
   145  					"sky": []network.PortRange{network.MustParsePortRange("8080/tcp")},
   146  				},
   147  			},
   148  			exp: map[string]network.GroupedPortRanges{
   149  				"enigma/0": {
   150  					"": []network.PortRange{network.MustParsePortRange("8080/tcp")},
   151  				},
   152  			},
   153  			expModified: false,
   154  		},
   155  		{
   156  			descr: "port range already opened by the unit for same endpoint",
   157  			existing: map[string]network.GroupedPortRanges{
   158  				"enigma/0": {
   159  					"sky": []network.PortRange{network.MustParsePortRange("8080/tcp")},
   160  				},
   161  			},
   162  			pendingOpen: map[string]network.GroupedPortRanges{
   163  				"enigma/0": {
   164  					"sky": []network.PortRange{network.MustParsePortRange("8080/tcp")},
   165  				},
   166  			},
   167  			exp: map[string]network.GroupedPortRanges{
   168  				"enigma/0": {
   169  					"sky": []network.PortRange{network.MustParsePortRange("8080/tcp")},
   170  				},
   171  			},
   172  			expModified: false,
   173  		},
   174  		{
   175  			descr: "port range already opened by the unit for other endpoint",
   176  			existing: map[string]network.GroupedPortRanges{
   177  				"enigma/0": {
   178  					"sky": []network.PortRange{network.MustParsePortRange("8080/tcp")},
   179  				},
   180  			},
   181  			pendingOpen: map[string]network.GroupedPortRanges{
   182  				"enigma/0": {
   183  					"sea": []network.PortRange{network.MustParsePortRange("8080/tcp")},
   184  				},
   185  			},
   186  			exp: map[string]network.GroupedPortRanges{
   187  				"enigma/0": {
   188  					"sky": []network.PortRange{network.MustParsePortRange("8080/tcp")},
   189  					"sea": []network.PortRange{network.MustParsePortRange("8080/tcp")},
   190  				},
   191  			},
   192  			expModified: true,
   193  		},
   194  	}
   195  
   196  	for i, spec := range specs {
   197  		c.Logf("%d: %s", i, spec.descr)
   198  
   199  		op := &openClosePortRangesOperation{
   200  			mpr: &machinePortRanges{
   201  				doc:               machinePortRangesDoc{UnitRanges: spec.existing},
   202  				pendingOpenRanges: spec.pendingOpen,
   203  			},
   204  		}
   205  
   206  		op.cloneExistingUnitPortRanges()
   207  		op.buildPortRangeToUnitMap()
   208  
   209  		modified, err := op.mergePendingOpenPortRanges()
   210  		c.Assert(err, jc.ErrorIsNil)
   211  		c.Assert(modified, gc.Equals, spec.expModified)
   212  		c.Assert(op.updatedUnitPortRanges, gc.DeepEquals, spec.exp)
   213  	}
   214  }
   215  
   216  func (MachinePortsOpsSuite) TestMergePendingClosePortRanges(c *gc.C) {
   217  	specs := []struct {
   218  		descr              string
   219  		endpointNamesByApp map[string]set.Strings
   220  		existing           map[string]network.GroupedPortRanges
   221  		pendingClose       map[string]network.GroupedPortRanges
   222  		exp                map[string]network.GroupedPortRanges
   223  		expModified        bool
   224  	}{
   225  		{
   226  			descr: "port range opened by the unit for same endpoint",
   227  			existing: map[string]network.GroupedPortRanges{
   228  				"enigma/0": {
   229  					"lava": []network.PortRange{
   230  						network.MustParsePortRange("8080/tcp"),
   231  						network.MustParsePortRange("9999/tcp"),
   232  					},
   233  				},
   234  			},
   235  			pendingClose: map[string]network.GroupedPortRanges{
   236  				"enigma/0": {
   237  					"lava": []network.PortRange{network.MustParsePortRange("8080/tcp")},
   238  				},
   239  			},
   240  			exp: map[string]network.GroupedPortRanges{
   241  				"enigma/0": {
   242  					"lava": []network.PortRange{network.MustParsePortRange("9999/tcp")},
   243  				},
   244  			},
   245  			expModified: true,
   246  		},
   247  		{
   248  			descr: "port range opened by the unit for another endpoint",
   249  			existing: map[string]network.GroupedPortRanges{
   250  				"enigma/0": {
   251  					"lava": []network.PortRange{network.MustParsePortRange("8080/tcp")},
   252  				},
   253  			},
   254  			pendingClose: map[string]network.GroupedPortRanges{
   255  				"enigma/0": {
   256  					"volcano": []network.PortRange{network.MustParsePortRange("8080/tcp")},
   257  				},
   258  			},
   259  			exp: map[string]network.GroupedPortRanges{
   260  				"enigma/0": {
   261  					// Close request is a no-op
   262  					"lava": []network.PortRange{network.MustParsePortRange("8080/tcp")},
   263  				},
   264  			},
   265  			expModified: false,
   266  		},
   267  		{
   268  			descr: "port range opened by the unit for all endpoints and closed for specific endpoint",
   269  			endpointNamesByApp: map[string]set.Strings{
   270  				"enigma": set.NewStrings("volcano", "lava", "sea"),
   271  			},
   272  			existing: map[string]network.GroupedPortRanges{
   273  				"enigma/0": {
   274  					"": []network.PortRange{
   275  						network.MustParsePortRange("7337/tcp"),
   276  						network.MustParsePortRange("8080/tcp"),
   277  					},
   278  				},
   279  			},
   280  			pendingClose: map[string]network.GroupedPortRanges{
   281  				"enigma/0": {
   282  					"lava": []network.PortRange{network.MustParsePortRange("8080/tcp")},
   283  				},
   284  			},
   285  			exp: map[string]network.GroupedPortRanges{
   286  				"enigma/0": {
   287  					// range removed from wildcard and replaced with
   288  					// entries for the all _other_ known endpoints
   289  					"":        []network.PortRange{network.MustParsePortRange("7337/tcp")},
   290  					"volcano": []network.PortRange{network.MustParsePortRange("8080/tcp")},
   291  					"sea":     []network.PortRange{network.MustParsePortRange("8080/tcp")},
   292  				},
   293  			},
   294  			expModified: true,
   295  		},
   296  	}
   297  
   298  	for i, spec := range specs {
   299  		c.Logf("%d: %s", i, spec.descr)
   300  
   301  		op := &openClosePortRangesOperation{
   302  			mpr: &machinePortRanges{
   303  				doc:                machinePortRangesDoc{UnitRanges: spec.existing},
   304  				pendingCloseRanges: spec.pendingClose,
   305  			},
   306  			endpointsNamesByApp: spec.endpointNamesByApp,
   307  		}
   308  
   309  		op.cloneExistingUnitPortRanges()
   310  		op.buildPortRangeToUnitMap()
   311  
   312  		modified, err := op.mergePendingClosePortRanges()
   313  		c.Assert(err, jc.ErrorIsNil)
   314  		c.Assert(modified, gc.Equals, spec.expModified)
   315  		c.Assert(op.updatedUnitPortRanges, gc.DeepEquals, spec.exp)
   316  	}
   317  }
   318  
   319  func (MachinePortsOpsSuite) TestMergePendingClosePortRangesConflict(c *gc.C) {
   320  	op := &openClosePortRangesOperation{
   321  		mpr: &machinePortRanges{
   322  			doc: machinePortRangesDoc{
   323  				UnitRanges: map[string]network.GroupedPortRanges{
   324  					"enigma/0": {
   325  						"": []network.PortRange{
   326  							network.MustParsePortRange("1234-1337/tcp"),
   327  							network.MustParsePortRange("8080/tcp"),
   328  							network.MustParsePortRange("17017/tcp"),
   329  						},
   330  					},
   331  				},
   332  			},
   333  			pendingCloseRanges: map[string]network.GroupedPortRanges{
   334  				"codebreaker/0": {
   335  					"tea": []network.PortRange{
   336  						network.MustParsePortRange("1242/tcp"),
   337  					},
   338  				},
   339  			},
   340  		},
   341  	}
   342  
   343  	op.cloneExistingUnitPortRanges()
   344  	op.buildPortRangeToUnitMap()
   345  
   346  	_, err := op.mergePendingClosePortRanges()
   347  	c.Assert(err, gc.ErrorMatches, `.*port ranges 1234-1337/tcp \("enigma/0"\) and 1242/tcp \("codebreaker/0"\) conflict`)
   348  }
   349  
   350  func (MachinePortsOpsSuite) TestValidatePendingChanges(c *gc.C) {
   351  	specs := []struct {
   352  		descr              string
   353  		endpointNamesByApp map[string]set.Strings
   354  		pendingOpen        map[string]network.GroupedPortRanges
   355  		pendingClose       map[string]network.GroupedPortRanges
   356  		expErr             string
   357  	}{
   358  		{
   359  			descr: "unknown endpoint in open request",
   360  			endpointNamesByApp: map[string]set.Strings{
   361  				"foo": set.NewStrings("dmz"),
   362  			},
   363  			pendingOpen: map[string]network.GroupedPortRanges{
   364  				"foo/0": {
   365  					"dmz":     []network.PortRange{network.MustParsePortRange("1337/tcp")},
   366  					"unknown": []network.PortRange{network.MustParsePortRange("8080/tcp")},
   367  				},
   368  			},
   369  			expErr: `open port range: endpoint "unknown" for unit "foo/0" not found`,
   370  		},
   371  		{
   372  			descr: "unknown endpoint in close request",
   373  			endpointNamesByApp: map[string]set.Strings{
   374  				"foo": set.NewStrings("dmz"),
   375  			},
   376  			pendingOpen: map[string]network.GroupedPortRanges{
   377  				"foo/0": {
   378  					"dmz": []network.PortRange{network.MustParsePortRange("1337/tcp")},
   379  				},
   380  			},
   381  			pendingClose: map[string]network.GroupedPortRanges{
   382  				"foo/0": {
   383  					"dmz":     []network.PortRange{network.MustParsePortRange("1337/tcp")},
   384  					"unknown": []network.PortRange{network.MustParsePortRange("8080/tcp")},
   385  				},
   386  			},
   387  			expErr: `close port range: endpoint "unknown" for unit "foo/0" not found`,
   388  		},
   389  		{
   390  			descr: "valid endpoints in open/close requests",
   391  			endpointNamesByApp: map[string]set.Strings{
   392  				"foo": set.NewStrings("dmz"),
   393  			},
   394  			pendingOpen: map[string]network.GroupedPortRanges{
   395  				"foo/0": {
   396  					"dmz": []network.PortRange{network.MustParsePortRange("1337/tcp")},
   397  				},
   398  			},
   399  			pendingClose: map[string]network.GroupedPortRanges{
   400  				"foo/0": {
   401  					"dmz": []network.PortRange{network.MustParsePortRange("1337/tcp")},
   402  				},
   403  			},
   404  		},
   405  	}
   406  
   407  	for i, spec := range specs {
   408  		c.Logf("%d: %s", i, spec.descr)
   409  		op := &openClosePortRangesOperation{
   410  			mpr: &machinePortRanges{
   411  				pendingOpenRanges:  spec.pendingOpen,
   412  				pendingCloseRanges: spec.pendingClose,
   413  			},
   414  			endpointsNamesByApp: spec.endpointNamesByApp,
   415  		}
   416  
   417  		err := op.validatePendingChanges()
   418  		if spec.expErr == "" {
   419  			c.Assert(err, jc.ErrorIsNil)
   420  		} else {
   421  			c.Assert(err, gc.ErrorMatches, spec.expErr)
   422  		}
   423  	}
   424  }