github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/network/debinterfaces/parser_test.go (about)

     1  // Copyright 2017 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package debinterfaces_test
     5  
     6  import (
     7  	"fmt"
     8  	"os"
     9  	"path/filepath"
    10  
    11  	"github.com/juju/testing"
    12  	jc "github.com/juju/testing/checkers"
    13  	gc "gopkg.in/check.v1"
    14  
    15  	"github.com/juju/juju/network/debinterfaces"
    16  )
    17  
    18  type ParserSuite struct {
    19  	testing.IsolationSuite
    20  
    21  	expander debinterfaces.WordExpander
    22  }
    23  
    24  var _ = gc.Suite(&ParserSuite{})
    25  
    26  // Ensure wordExpanderWithError is a WordExpander
    27  var _ debinterfaces.WordExpander = (*wordExpanderErrors)(nil)
    28  
    29  type wordExpanderErrors struct {
    30  	errmsg string
    31  }
    32  
    33  func wordExpanderWithError(errmsg string) debinterfaces.WordExpander {
    34  	return &wordExpanderErrors{errmsg: errmsg}
    35  }
    36  
    37  func (w *wordExpanderErrors) Expand(s string) ([]string, error) {
    38  	return nil, fmt.Errorf("word expansion failed: %s", w.errmsg)
    39  }
    40  
    41  func (s *ParserSuite) SetUpTest(c *gc.C) {
    42  	s.expander = debinterfaces.NewWordExpander()
    43  }
    44  
    45  func (s *ParserSuite) TestNilInput(c *gc.C) {
    46  	interfaces, err := debinterfaces.ParseSource("", nil, s.expander)
    47  	c.Assert(interfaces, gc.IsNil)
    48  	c.Check(err, gc.ErrorMatches, "filename and input is nil")
    49  }
    50  
    51  func (s *ParserSuite) TestNilInputAndNoFilename(c *gc.C) {
    52  	stanzas, err := debinterfaces.ParseSource("", "", s.expander)
    53  	c.Assert(err, gc.IsNil)
    54  	c.Check(stanzas, gc.HasLen, 0)
    55  }
    56  
    57  func (s *ParserSuite) TestUnsupportedInputType(c *gc.C) {
    58  	stanzas, err := debinterfaces.ParseSource("", []float64{3.141}, s.expander)
    59  	c.Assert(err, gc.ErrorMatches, "invalid source type")
    60  	c.Check(stanzas, gc.IsNil)
    61  }
    62  
    63  func (s *ParserSuite) TestFilenameEmpty(c *gc.C) {
    64  	emptyFile := filepath.Join(c.MkDir(), "TestFilenameEmpty")
    65  	err := os.WriteFile(emptyFile, []byte(""), 0644)
    66  	c.Assert(err, gc.IsNil)
    67  	stanzas, err := debinterfaces.ParseSource(emptyFile, nil, s.expander)
    68  	c.Assert(err, gc.IsNil)
    69  	c.Check(stanzas, gc.HasLen, 0)
    70  }
    71  
    72  func (s *ParserSuite) TestParseErrorObject(c *gc.C) {
    73  	content := `hunky dory`
    74  	_, err := debinterfaces.ParseSource("", content, s.expander)
    75  	c.Assert(err, gc.NotNil)
    76  	_, parseError := err.(*debinterfaces.ParseError)
    77  	c.Assert(parseError, gc.NotNil)
    78  	c.Assert(err, gc.ErrorMatches, "misplaced option")
    79  }
    80  
    81  func (s *ParserSuite) TestCommentsAndBlankLinesOnly(c *gc.C) {
    82  	content := `
    83  
    84  # Comment 1.
    85  
    86  # Comment 2, after empty line.
    87  
    88    # An indented comment, followed by a line with leading whitespace
    89  
    90  `
    91  	stanzas, err := debinterfaces.ParseSource("", content, s.expander)
    92  	c.Assert(err, gc.IsNil)
    93  	c.Assert(stanzas, gc.HasLen, 0)
    94  }
    95  
    96  func (s *ParserSuite) TestAllowStanzaMissingArg(c *gc.C) {
    97  	_, err := debinterfaces.ParseSource("", "allow-hotplug", s.expander)
    98  	c.Assert(err, gc.NotNil)
    99  	c.Check(err, gc.ErrorMatches, "missing device name")
   100  }
   101  
   102  func (s *ParserSuite) TestAutoStanzaMissingArg(c *gc.C) {
   103  	_, err := debinterfaces.ParseSource("", "auto", s.expander)
   104  	c.Assert(err, gc.NotNil)
   105  	c.Check(err, gc.ErrorMatches, "missing device name")
   106  }
   107  
   108  func (s *ParserSuite) TestIfaceStanzaMissingArg(c *gc.C) {
   109  	_, err := debinterfaces.ParseSource("", "iface", s.expander)
   110  	c.Assert(err, gc.NotNil)
   111  	c.Check(err, gc.ErrorMatches, "missing device name")
   112  }
   113  
   114  func (s *ParserSuite) TestMappingStanzaMissingArg(c *gc.C) {
   115  	_, err := debinterfaces.ParseSource("", "mapping", s.expander)
   116  	c.Assert(err, gc.NotNil)
   117  	c.Check(err, gc.ErrorMatches, "missing device name")
   118  }
   119  
   120  func (s *ParserSuite) TestNoAutoDownStanzaMissingArg(c *gc.C) {
   121  	_, err := debinterfaces.ParseSource("", "no-auto-down", s.expander)
   122  	c.Assert(err, gc.NotNil)
   123  	c.Check(err, gc.ErrorMatches, "missing device name")
   124  }
   125  
   126  func (s *ParserSuite) TestNoScriptsStanzaMissingArg(c *gc.C) {
   127  	_, err := debinterfaces.ParseSource("", "no-scripts", s.expander)
   128  	c.Assert(err, gc.NotNil)
   129  	c.Check(err, gc.ErrorMatches, "missing device name")
   130  }
   131  
   132  func (s *ParserSuite) TestSourceStanzaMissingArg(c *gc.C) {
   133  	_, err := debinterfaces.ParseSource("", "source", s.expander)
   134  	c.Assert(err, gc.NotNil)
   135  	c.Check(err, gc.ErrorMatches, "missing filename")
   136  }
   137  
   138  func (s *ParserSuite) TestSourceDirectoryStanzaMissingArg(c *gc.C) {
   139  	_, err := debinterfaces.ParseSource("", "source-directory", s.expander)
   140  	c.Assert(err, gc.NotNil)
   141  	c.Check(err, gc.ErrorMatches, "missing directory")
   142  }
   143  
   144  func (s *ParserSuite) TestAllowStanza(c *gc.C) {
   145  	definition := "allow-hotplug eth0 eth1 eth2"
   146  	stanzas, err := debinterfaces.ParseSource("", definition, s.expander)
   147  	c.Assert(err, gc.IsNil)
   148  	c.Assert(stanzas, gc.HasLen, 1)
   149  	c.Assert(stanzas[0], gc.FitsTypeOf, debinterfaces.AllowStanza{})
   150  	allow := stanzas[0].(debinterfaces.AllowStanza)
   151  	c.Check(allow.Location().Filename, gc.Equals, "")
   152  	c.Check(allow.Location().LineNum, gc.Equals, 1)
   153  	c.Assert(allow.Definition(), gc.HasLen, 1)
   154  	c.Check(allow.Definition()[0], gc.Equals, definition)
   155  	c.Check(allow.DeviceNames, gc.DeepEquals, []string{"eth0", "eth1", "eth2"})
   156  }
   157  
   158  func (s *ParserSuite) TestAutoStanza(c *gc.C) {
   159  	definition := "auto eth0 eth1 eth2"
   160  	stanzas, err := debinterfaces.ParseSource("", definition, s.expander)
   161  	c.Assert(err, gc.IsNil)
   162  	c.Assert(stanzas, gc.HasLen, 1)
   163  	c.Assert(stanzas[0], gc.FitsTypeOf, debinterfaces.AutoStanza{})
   164  	auto := stanzas[0].(debinterfaces.AutoStanza)
   165  	c.Check(auto.Location().Filename, gc.Equals, "")
   166  	c.Check(auto.Location().LineNum, gc.Equals, 1)
   167  	c.Assert(auto.Definition(), gc.HasLen, 1)
   168  	c.Check(auto.Definition()[0], gc.Equals, definition)
   169  	c.Check(auto.DeviceNames, gc.DeepEquals, []string{"eth0", "eth1", "eth2"})
   170  }
   171  
   172  func (s *ParserSuite) TestWithIfaceStanzaAndOneOption(c *gc.C) {
   173  	content := `
   174  iface eth0 inet manual
   175    # A comment.
   176    address 192.168.1.254/24 `
   177  	stanzas, err := debinterfaces.ParseSource("", content, s.expander)
   178  	c.Assert(err, gc.IsNil)
   179  	c.Assert(stanzas, gc.HasLen, 1)
   180  	c.Assert(stanzas[0], gc.FitsTypeOf, debinterfaces.IfaceStanza{})
   181  	iface := stanzas[0].(debinterfaces.IfaceStanza)
   182  	c.Assert(iface.Definition(), gc.HasLen, 2)
   183  	c.Check(iface.Definition()[0], gc.Equals, "iface eth0 inet manual")
   184  	c.Check(iface.Definition()[1], gc.Equals, "address 192.168.1.254/24")
   185  	c.Check(iface.Location().Filename, gc.Equals, "")
   186  	c.Check(iface.Location().LineNum, gc.Equals, 2)
   187  	c.Check(iface.Options, gc.HasLen, 1)
   188  	c.Check(iface.Options[0], gc.Equals, "address 192.168.1.254/24")
   189  	c.Check(iface.HasBondMasterOption, gc.Equals, false)
   190  	c.Check(iface.HasBondOptions, gc.Equals, false)
   191  	c.Check(iface.IsAlias, gc.Equals, false)
   192  	c.Check(iface.IsBridged, gc.Equals, false)
   193  	c.Check(iface.IsVLAN, gc.Equals, false)
   194  }
   195  
   196  func (s *ParserSuite) TestWithIfaceStanzaAndMultipleOptions(c *gc.C) {
   197  	content := `
   198  iface eth0 inet manual
   199    # A comment.
   200    address 192.168.1.254/24
   201    dns-nameservers 8.8.8.8
   202    # Another comment.
   203    dns-search ubuntu.com
   204    # An ending comment`
   205  	stanzas, err := debinterfaces.ParseSource("", content, s.expander)
   206  	c.Assert(err, gc.IsNil)
   207  	c.Assert(stanzas, gc.HasLen, 1)
   208  	c.Assert(stanzas[0], gc.FitsTypeOf, debinterfaces.IfaceStanza{})
   209  	iface := stanzas[0].(debinterfaces.IfaceStanza)
   210  	c.Assert(iface.Definition(), gc.HasLen, 4)
   211  	c.Check(iface.Definition()[0], gc.Equals, "iface eth0 inet manual")
   212  	c.Check(iface.Definition()[1], gc.Equals, "address 192.168.1.254/24")
   213  	c.Check(iface.Definition()[2], gc.Equals, "dns-nameservers 8.8.8.8")
   214  	c.Check(iface.Definition()[3], gc.Equals, "dns-search ubuntu.com")
   215  	c.Check(iface.Location().Filename, gc.Equals, "")
   216  	c.Check(iface.Location().LineNum, gc.Equals, 2)
   217  	c.Assert(iface.Options, gc.HasLen, 3)
   218  	c.Check(iface.Options[0], gc.Equals, "address 192.168.1.254/24")
   219  	c.Check(iface.Options[1], gc.Equals, "dns-nameservers 8.8.8.8")
   220  	c.Check(iface.Options[2], gc.Equals, "dns-search ubuntu.com")
   221  	c.Check(iface.HasBondMasterOption, gc.Equals, false)
   222  	c.Check(iface.HasBondOptions, gc.Equals, false)
   223  	c.Check(iface.IsAlias, gc.Equals, false)
   224  	c.Check(iface.IsBridged, gc.Equals, false)
   225  	c.Check(iface.IsVLAN, gc.Equals, false)
   226  }
   227  
   228  func (s *ParserSuite) TestWithIfaceStanzaAndNoOptions(c *gc.C) {
   229  	content := `
   230  iface eth0 inet manual
   231  # A comment.`
   232  	stanzas, err := debinterfaces.ParseSource("", content, s.expander)
   233  	c.Assert(err, gc.IsNil)
   234  	c.Assert(stanzas, gc.HasLen, 1)
   235  	c.Assert(stanzas[0], gc.FitsTypeOf, debinterfaces.IfaceStanza{})
   236  	iface := stanzas[0].(debinterfaces.IfaceStanza)
   237  	c.Assert(iface.Definition(), gc.HasLen, 1)
   238  	c.Check(iface.Definition()[0], gc.Equals, "iface eth0 inet manual")
   239  	c.Check(iface.Location().Filename, gc.Equals, "")
   240  	c.Check(iface.Location().LineNum, gc.Equals, 2)
   241  	c.Check(iface.Options, gc.HasLen, 0)
   242  	c.Check(iface.HasBondMasterOption, gc.Equals, false)
   243  	c.Check(iface.HasBondOptions, gc.Equals, false)
   244  	c.Check(iface.IsAlias, gc.Equals, false)
   245  	c.Check(iface.IsBridged, gc.Equals, false)
   246  	c.Check(iface.IsVLAN, gc.Equals, false)
   247  }
   248  
   249  func (s *ParserSuite) TestAutoStanzaFollowedByIfaceStanza(c *gc.C) {
   250  	content := `
   251  auto eth0
   252  
   253  iface eth0 inet static
   254    address 192.168.1.254/24
   255    gateway 192.168.1.1`
   256  	stanzas, err := debinterfaces.ParseSource("", content, s.expander)
   257  	c.Assert(err, gc.IsNil)
   258  	c.Assert(stanzas, gc.HasLen, 2)
   259  	c.Assert(stanzas[0], gc.FitsTypeOf, debinterfaces.AutoStanza{})
   260  	c.Assert(stanzas[1], gc.FitsTypeOf, debinterfaces.IfaceStanza{})
   261  
   262  	auto := stanzas[0].(debinterfaces.AutoStanza)
   263  	c.Assert(auto.Definition(), gc.HasLen, 1)
   264  	c.Check(auto.Definition()[0], gc.Equals, "auto eth0")
   265  	c.Check(auto.Location().Filename, gc.Equals, "")
   266  	c.Check(auto.Location().LineNum, gc.Equals, 2)
   267  
   268  	iface := stanzas[1].(debinterfaces.IfaceStanza)
   269  	c.Assert(iface.Definition(), gc.HasLen, 3)
   270  	c.Check(iface.Definition()[0], gc.Equals, "iface eth0 inet static")
   271  	c.Check(iface.Definition()[1], gc.Equals, "address 192.168.1.254/24")
   272  	c.Check(iface.Definition()[2], gc.Equals, "gateway 192.168.1.1")
   273  	c.Check(iface.Location().Filename, gc.Equals, "")
   274  	c.Check(iface.Location().LineNum, gc.Equals, 4)
   275  	c.Assert(iface.Options, gc.HasLen, 2)
   276  	c.Check(iface.Options[0], gc.Equals, "address 192.168.1.254/24")
   277  	c.Check(iface.Options[1], gc.Equals, "gateway 192.168.1.1")
   278  	c.Check(iface.HasBondMasterOption, gc.Equals, false)
   279  	c.Check(iface.HasBondOptions, gc.Equals, false)
   280  	c.Check(iface.IsAlias, gc.Equals, false)
   281  	c.Check(iface.IsBridged, gc.Equals, false)
   282  	c.Check(iface.IsVLAN, gc.Equals, false)
   283  }
   284  
   285  func (s *ParserSuite) TestVLANIfaceStanza(c *gc.C) {
   286  	content := `
   287  iface eth0.100 inet static
   288    address 192.168.1.254/24
   289    vlan-raw-device eth1`
   290  	stanzas, err := debinterfaces.ParseSource("", content, s.expander)
   291  	c.Assert(err, gc.IsNil)
   292  	c.Assert(stanzas, gc.HasLen, 1)
   293  	c.Assert(stanzas[0], gc.FitsTypeOf, debinterfaces.IfaceStanza{})
   294  
   295  	iface := stanzas[0].(debinterfaces.IfaceStanza)
   296  	c.Assert(iface.Definition(), gc.HasLen, 3)
   297  	c.Check(iface.Definition()[0], gc.Equals, "iface eth0.100 inet static")
   298  	c.Check(iface.Definition()[1], gc.Equals, "address 192.168.1.254/24")
   299  	c.Check(iface.Definition()[2], gc.Equals, "vlan-raw-device eth1")
   300  	c.Check(iface.Location().Filename, gc.Equals, "")
   301  	c.Check(iface.Location().LineNum, gc.Equals, 2)
   302  	c.Assert(iface.Options, gc.HasLen, 2)
   303  	c.Check(iface.Options[0], gc.Equals, "address 192.168.1.254/24")
   304  	c.Check(iface.Options[1], gc.Equals, "vlan-raw-device eth1")
   305  	c.Check(iface.HasBondMasterOption, gc.Equals, false)
   306  	c.Check(iface.HasBondOptions, gc.Equals, false)
   307  	c.Check(iface.IsAlias, gc.Equals, false)
   308  	c.Check(iface.IsBridged, gc.Equals, false)
   309  	c.Check(iface.IsVLAN, gc.Equals, true)
   310  }
   311  
   312  func (s *ParserSuite) TestBondedIfaceStanza(c *gc.C) {
   313  	content := `
   314  auto eth0
   315  iface eth0 inet manual
   316      bond-lacp_rate slow
   317      bond-xmit_hash_policy layer2
   318      bond-miimon 100
   319      bond-master bond0
   320      bond-mode active-backup
   321  
   322  auto eth1
   323  iface eth1 inet manual
   324      bond-lacp_rate slow
   325      bond-xmit_hash_policy layer2
   326      bond-miimon 100
   327      bond-master bond0
   328      mtu 1500
   329      bond-mode active-backup
   330  
   331  auto bond0
   332  iface bond0 inet dhcp
   333      bond-lacp_rate slow
   334      bond-xmit_hash_policy layer2
   335      bond-miimon 100
   336      mtu 1500
   337      bond-mode active-backup
   338      hwaddress 52:54:00:1c:f1:5b
   339      bond-slaves none`
   340  	stanzas, err := debinterfaces.ParseSource("", content, s.expander)
   341  	c.Assert(err, gc.IsNil)
   342  	c.Assert(stanzas, gc.HasLen, 6)
   343  	c.Assert(stanzas[0], gc.FitsTypeOf, debinterfaces.AutoStanza{})
   344  	c.Assert(stanzas[1], gc.FitsTypeOf, debinterfaces.IfaceStanza{})
   345  	c.Assert(stanzas[2], gc.FitsTypeOf, debinterfaces.AutoStanza{})
   346  	c.Assert(stanzas[3], gc.FitsTypeOf, debinterfaces.IfaceStanza{})
   347  	c.Assert(stanzas[4], gc.FitsTypeOf, debinterfaces.AutoStanza{})
   348  	c.Assert(stanzas[5], gc.FitsTypeOf, debinterfaces.IfaceStanza{})
   349  
   350  	iface0 := stanzas[1].(debinterfaces.IfaceStanza)
   351  	c.Assert(iface0.Definition(), gc.HasLen, 6)
   352  	c.Check(iface0.Definition()[0], gc.Equals, "iface eth0 inet manual")
   353  	c.Check(iface0.Location().Filename, gc.Equals, "")
   354  	c.Check(iface0.Location().LineNum, gc.Equals, 3)
   355  	c.Check(iface0.Options, gc.HasLen, 5)
   356  	c.Check(iface0.HasBondMasterOption, gc.Equals, true)
   357  	c.Check(iface0.HasBondOptions, gc.Equals, true)
   358  	c.Check(iface0.IsAlias, gc.Equals, false)
   359  	c.Check(iface0.IsBridged, gc.Equals, false)
   360  	c.Check(iface0.IsVLAN, gc.Equals, false)
   361  
   362  	iface1 := stanzas[3].(debinterfaces.IfaceStanza)
   363  	c.Assert(iface1.Definition(), gc.HasLen, 7)
   364  	c.Check(iface1.Definition()[0], gc.Equals, "iface eth1 inet manual")
   365  	c.Check(iface1.Location().Filename, gc.Equals, "")
   366  	c.Check(iface1.Location().LineNum, gc.Equals, 11)
   367  	c.Check(iface1.Options, gc.HasLen, 6)
   368  	c.Check(iface1.HasBondMasterOption, gc.Equals, true)
   369  	c.Check(iface1.HasBondOptions, gc.Equals, true)
   370  	c.Check(iface1.IsAlias, gc.Equals, false)
   371  	c.Check(iface1.IsBridged, gc.Equals, false)
   372  	c.Check(iface1.IsVLAN, gc.Equals, false)
   373  
   374  	iface2 := stanzas[5].(debinterfaces.IfaceStanza)
   375  	c.Assert(iface2.Definition(), gc.HasLen, 8)
   376  	c.Check(iface2.Definition()[0], gc.Equals, "iface bond0 inet dhcp")
   377  	c.Check(iface2.Location().Filename, gc.Equals, "")
   378  	c.Check(iface2.Location().LineNum, gc.Equals, 20)
   379  	c.Check(iface2.Options, gc.HasLen, 7)
   380  	c.Check(iface2.HasBondOptions, gc.Equals, true)
   381  	c.Check(iface2.HasBondMasterOption, gc.Equals, false)
   382  	c.Check(iface2.IsAlias, gc.Equals, false)
   383  	c.Check(iface2.IsBridged, gc.Equals, false)
   384  	c.Check(iface2.IsVLAN, gc.Equals, false)
   385  }
   386  
   387  func (s *ParserSuite) TestMappingStanza(c *gc.C) {
   388  	content := `
   389  mapping eth0 eth1
   390    script /path/to/get-mac-address.sh
   391    map 11:22:33:44:55:66 lan
   392    map AA:BB:CC:DD:EE:FF internet`
   393  	stanzas, err := debinterfaces.ParseSource("", content, s.expander)
   394  	c.Assert(err, gc.IsNil)
   395  	c.Assert(stanzas, gc.HasLen, 1)
   396  	c.Assert(stanzas[0], gc.FitsTypeOf, debinterfaces.MappingStanza{})
   397  
   398  	mapping := stanzas[0].(debinterfaces.MappingStanza)
   399  	c.Assert(mapping.Definition(), gc.HasLen, 4)
   400  	c.Check(mapping.Definition()[0], gc.Equals, "mapping eth0 eth1")
   401  	c.Check(mapping.Location().Filename, gc.Equals, "")
   402  	c.Check(mapping.Location().LineNum, gc.Equals, 2)
   403  	c.Check(mapping.DeviceNames, gc.DeepEquals, []string{"eth0", "eth1"})
   404  	c.Assert(mapping.Options, gc.HasLen, 3)
   405  	c.Check(mapping.Options[0], gc.Equals, "script /path/to/get-mac-address.sh")
   406  	c.Check(mapping.Options[1], gc.Equals, "map 11:22:33:44:55:66 lan")
   407  	c.Check(mapping.Options[2], gc.Equals, "map AA:BB:CC:DD:EE:FF internet")
   408  }
   409  
   410  func (s *ParserSuite) TestNoAutoDownStanza(c *gc.C) {
   411  	content := `no-auto-down eth0 eth1`
   412  	stanzas, err := debinterfaces.ParseSource("", content, s.expander)
   413  	c.Assert(err, gc.IsNil)
   414  	c.Assert(stanzas, gc.HasLen, 1)
   415  	c.Assert(stanzas[0], gc.FitsTypeOf, debinterfaces.NoAutoDownStanza{})
   416  
   417  	noautodown := stanzas[0].(debinterfaces.NoAutoDownStanza)
   418  	c.Assert(noautodown.Definition(), gc.HasLen, 1)
   419  	c.Check(noautodown.Definition()[0], gc.Equals, "no-auto-down eth0 eth1")
   420  	c.Check(noautodown.Location().Filename, gc.Equals, "")
   421  	c.Check(noautodown.Location().LineNum, gc.Equals, 1)
   422  	c.Check(noautodown.DeviceNames, gc.DeepEquals, []string{"eth0", "eth1"})
   423  }
   424  
   425  func (s *ParserSuite) TestNoScriptsStanza(c *gc.C) {
   426  	content := `no-scripts eth0 eth1`
   427  	stanzas, err := debinterfaces.ParseSource("", content, s.expander)
   428  	c.Assert(err, gc.IsNil)
   429  	c.Assert(stanzas, gc.HasLen, 1)
   430  	c.Assert(stanzas[0], gc.FitsTypeOf, debinterfaces.NoScriptsStanza{})
   431  
   432  	noscripts := stanzas[0].(debinterfaces.NoScriptsStanza)
   433  	c.Assert(noscripts.Definition(), gc.HasLen, 1)
   434  	c.Check(noscripts.Definition()[0], gc.Equals, "no-scripts eth0 eth1")
   435  	c.Check(noscripts.Location().Filename, gc.Equals, "")
   436  	c.Check(noscripts.Location().LineNum, gc.Equals, 1)
   437  	c.Check(noscripts.DeviceNames, gc.DeepEquals, []string{"eth0", "eth1"})
   438  }
   439  
   440  func (s *ParserSuite) TestFailParseDirectoryAsInput(c *gc.C) {
   441  	stanzas, err := debinterfaces.Parse("testdata/TestInputSourceStanza")
   442  	c.Assert(err, gc.NotNil)
   443  	c.Check(err, gc.ErrorMatches, ".* testdata/TestInputSourceStanza: .*")
   444  	c.Assert(stanzas, gc.IsNil)
   445  }
   446  
   447  func (s *ParserSuite) TestSourceStanzaNonExistentFile(c *gc.C) {
   448  	_, err := debinterfaces.Parse("testdata/TestInputSourceStanza/non-existent-file")
   449  	c.Assert(err, gc.NotNil)
   450  	c.Check(err, gc.ErrorMatches, ".* testdata/TestInputSourceStanza/non-existent-file: .*")
   451  }
   452  
   453  func (s *ParserSuite) TestSourceStanzaWhereGlobHasZeroMatches(c *gc.C) {
   454  	stanzas, err := debinterfaces.ParseSource("testdata/TestSourceStanzaWhereGlobHasZeroMatches/interfaces", nil, s.expander)
   455  	c.Assert(err, gc.IsNil)
   456  	c.Assert(stanzas, gc.HasLen, 1)
   457  	c.Assert(stanzas[0], gc.FitsTypeOf, debinterfaces.SourceStanza{})
   458  	src := stanzas[0].(debinterfaces.SourceStanza)
   459  	c.Assert(src.Sources, gc.HasLen, 0)
   460  	c.Assert(src.Stanzas, gc.HasLen, 0)
   461  }
   462  
   463  func (s *ParserSuite) TestSourceStanzaWithRelativeFilenames(c *gc.C) {
   464  	stanzas, err := debinterfaces.Parse("testdata/TestInputSourceStanza/interfaces")
   465  	c.Assert(err, gc.IsNil)
   466  	c.Assert(stanzas, gc.HasLen, 3)
   467  	c.Assert(stanzas[0], gc.FitsTypeOf, debinterfaces.AutoStanza{})
   468  	c.Assert(stanzas[1], gc.FitsTypeOf, debinterfaces.IfaceStanza{})
   469  	c.Assert(stanzas[2], gc.FitsTypeOf, debinterfaces.SourceStanza{})
   470  
   471  	c.Assert(stanzas[0].Location().Filename, gc.Equals, "testdata/TestInputSourceStanza/interfaces")
   472  	c.Assert(stanzas[0].Location().LineNum, gc.Equals, 5)
   473  
   474  	c.Assert(stanzas[1].Location().Filename, gc.Equals, "testdata/TestInputSourceStanza/interfaces")
   475  	c.Assert(stanzas[1].Location().LineNum, gc.Equals, 6)
   476  
   477  	c.Assert(stanzas[2].Location().Filename, gc.Equals, "testdata/TestInputSourceStanza/interfaces")
   478  	c.Assert(stanzas[2].Location().LineNum, gc.Equals, 12)
   479  
   480  	source := stanzas[2].(debinterfaces.SourceStanza)
   481  	c.Check(source.Path, gc.Equals, "interfaces.d/*.cfg")
   482  
   483  	c.Assert(source.Stanzas, gc.HasLen, 6)
   484  
   485  	c.Assert(source.Stanzas[0], gc.FitsTypeOf, debinterfaces.AutoStanza{})
   486  	c.Assert(source.Stanzas[1], gc.FitsTypeOf, debinterfaces.IfaceStanza{})
   487  	c.Assert(source.Stanzas[2], gc.FitsTypeOf, debinterfaces.AutoStanza{})
   488  	c.Assert(source.Stanzas[3], gc.FitsTypeOf, debinterfaces.IfaceStanza{})
   489  	c.Assert(source.Stanzas[4], gc.FitsTypeOf, debinterfaces.AutoStanza{})
   490  	c.Assert(source.Stanzas[5], gc.FitsTypeOf, debinterfaces.IfaceStanza{})
   491  
   492  	basePath := filepath.Join("testdata", "TestInputSourceStanza", "interfaces.d")
   493  	c.Check(source.Sources, gc.DeepEquals, []string{
   494  		filepath.Join(basePath, "eth0.cfg"),
   495  		filepath.Join(basePath, "eth1.cfg"),
   496  		filepath.Join(basePath, "eth2.cfg"),
   497  	})
   498  
   499  	// Note: we don't have tests for stanzas nested > 1 deep.
   500  
   501  	eth0 := source.Stanzas[1].(debinterfaces.IfaceStanza)
   502  	eth1 := source.Stanzas[3].(debinterfaces.IfaceStanza)
   503  	eth2 := source.Stanzas[5].(debinterfaces.IfaceStanza)
   504  
   505  	c.Assert(eth0.Definition(), gc.HasLen, 1)
   506  	c.Check(eth0.Definition()[0], gc.Equals, "iface eth0 inet dhcp")
   507  	c.Check(eth0.Location().Filename, gc.Equals, filepath.Join(basePath, "eth0.cfg"))
   508  	c.Check(eth0.Location().LineNum, gc.Equals, 2)
   509  
   510  	c.Assert(eth1.Definition(), gc.HasLen, 3)
   511  	c.Check(eth1.Definition()[0], gc.Equals, "iface eth1 inet static")
   512  	c.Check(eth1.Definition()[1], gc.Equals, "address 192.168.1.64")
   513  	c.Check(eth1.Definition()[2], gc.Equals, "dns-nameservers 192.168.1.254")
   514  	c.Check(eth1.Location().Filename, gc.Equals, filepath.Join(basePath, "eth1.cfg"))
   515  	c.Check(eth1.Location().LineNum, gc.Equals, 2)
   516  
   517  	c.Assert(eth2.Definition(), gc.HasLen, 1)
   518  	c.Check(eth2.Definition()[0], gc.Equals, "iface eth2 inet manual")
   519  	c.Check(eth2.Location().Filename, gc.Equals, filepath.Join(basePath, "eth2.cfg"))
   520  	c.Check(eth2.Location().LineNum, gc.Equals, 2)
   521  }
   522  
   523  func (s *ParserSuite) TestSourceStanzaFromFileWithStanzaErrors(c *gc.C) {
   524  	_, err := debinterfaces.Parse("testdata/TestInputSourceStanzaWithErrors/interfaces")
   525  	c.Assert(err, gc.NotNil)
   526  	c.Assert(err, gc.FitsTypeOf, &debinterfaces.ParseError{})
   527  	parseError := err.(*debinterfaces.ParseError)
   528  	c.Assert(parseError, gc.DeepEquals, &debinterfaces.ParseError{
   529  		Filename: filepath.Join("testdata", "TestInputSourceStanzaWithErrors", "interfaces.d", "eth1.cfg"),
   530  		Line:     "iface",
   531  		LineNum:  2,
   532  		Message:  "missing device name",
   533  	})
   534  }
   535  
   536  func (s *ParserSuite) TestSourceDirectoryStanzaWithRelativeFilenames(c *gc.C) {
   537  	stanzas, err := debinterfaces.Parse("testdata/TestInputSourceDirectoryStanza/interfaces")
   538  	c.Assert(err, gc.IsNil)
   539  	c.Assert(stanzas, gc.HasLen, 3)
   540  	c.Assert(stanzas[0], gc.FitsTypeOf, debinterfaces.AutoStanza{})
   541  	c.Assert(stanzas[1], gc.FitsTypeOf, debinterfaces.IfaceStanza{})
   542  	c.Assert(stanzas[2], gc.FitsTypeOf, debinterfaces.SourceDirectoryStanza{})
   543  
   544  	c.Assert(stanzas[0].Location().Filename, gc.Equals, "testdata/TestInputSourceDirectoryStanza/interfaces")
   545  	c.Assert(stanzas[0].Location().LineNum, gc.Equals, 1)
   546  
   547  	c.Assert(stanzas[1].Location().Filename, gc.Equals, "testdata/TestInputSourceDirectoryStanza/interfaces")
   548  	c.Assert(stanzas[1].Location().LineNum, gc.Equals, 2)
   549  
   550  	c.Assert(stanzas[2].Location().Filename, gc.Equals, "testdata/TestInputSourceDirectoryStanza/interfaces")
   551  	c.Assert(stanzas[2].Location().LineNum, gc.Equals, 4)
   552  
   553  	source := stanzas[2].(debinterfaces.SourceDirectoryStanza)
   554  	c.Check(source.Path, gc.Equals, "interfaces.d")
   555  
   556  	c.Assert(source.Stanzas, gc.HasLen, 2)
   557  
   558  	c.Assert(source.Stanzas[0], gc.FitsTypeOf, debinterfaces.AutoStanza{})
   559  	c.Assert(source.Stanzas[1], gc.FitsTypeOf, debinterfaces.IfaceStanza{})
   560  
   561  	c.Check(source.Sources, gc.DeepEquals, []string{
   562  		filepath.Join("testdata", "TestInputSourceDirectoryStanza", "interfaces.d", "eth3"),
   563  	})
   564  
   565  	// Note: we don't have tests for stanzas nested > 1 deep.
   566  
   567  	eth3 := source.Stanzas[1].(debinterfaces.IfaceStanza)
   568  	c.Assert(eth3.Definition(), gc.HasLen, 3)
   569  	c.Check(eth3.Definition()[0], gc.Equals, "iface eth3 inet static")
   570  	c.Check(eth3.Definition()[1], gc.Equals, "address 192.168.1.128")
   571  	c.Check(eth3.Definition()[2], gc.Equals, "dns-nameservers 192.168.1.254")
   572  	c.Check(eth3.Location().Filename, gc.Equals, filepath.Join("testdata", "TestInputSourceDirectoryStanza", "interfaces.d", "eth3"))
   573  	c.Check(eth3.Location().LineNum, gc.Equals, 2)
   574  }
   575  
   576  func (s *ParserSuite) TestSourceDirectoryStanzaFromDirectoryWithStanzaErrors(c *gc.C) {
   577  	_, err := debinterfaces.Parse("testdata/TestInputSourceDirectoryStanzaWithErrors/interfaces")
   578  	c.Assert(err, gc.NotNil)
   579  	c.Assert(err, gc.FitsTypeOf, &debinterfaces.ParseError{})
   580  	parseError := err.(*debinterfaces.ParseError)
   581  	c.Assert(parseError, gc.DeepEquals, &debinterfaces.ParseError{
   582  		Filename: filepath.Join("testdata", "TestInputSourceDirectoryStanzaWithErrors", "interfaces.d", "eth3"),
   583  		Line:     "iface",
   584  		LineNum:  2,
   585  		Message:  "missing device name",
   586  	})
   587  }
   588  
   589  func (s *ParserSuite) TestSourceStanzaWithWordExpanderError(c *gc.C) {
   590  	_, err := debinterfaces.ParseSource("testdata/TestInputSourceStanza/interfaces", nil, wordExpanderWithError("boom"))
   591  	c.Assert(err, gc.NotNil)
   592  	c.Assert(err, gc.FitsTypeOf, &debinterfaces.ParseError{})
   593  	parseError := err.(*debinterfaces.ParseError)
   594  	c.Assert(parseError, gc.DeepEquals, &debinterfaces.ParseError{
   595  		Filename: "testdata/TestInputSourceStanza/interfaces",
   596  		Line:     "source interfaces.d/*.cfg",
   597  		LineNum:  12,
   598  		Message:  "word expansion failed: boom",
   599  	})
   600  }
   601  
   602  func (s *ParserSuite) TestSourceDirectoryStanzaWithWordExpanderError(c *gc.C) {
   603  	_, err := debinterfaces.ParseSource("testdata/TestInputSourceDirectoryStanza/interfaces", nil, wordExpanderWithError("boom"))
   604  	c.Assert(err, gc.NotNil)
   605  	c.Assert(err, gc.FitsTypeOf, &debinterfaces.ParseError{})
   606  	parseError := err.(*debinterfaces.ParseError)
   607  	c.Assert(parseError, gc.DeepEquals, &debinterfaces.ParseError{
   608  		Filename: "testdata/TestInputSourceDirectoryStanza/interfaces",
   609  		Line:     "source-directory interfaces.d",
   610  		LineNum:  4,
   611  		Message:  "word expansion failed: boom",
   612  	})
   613  }
   614  
   615  func (s *ParserSuite) TestSourceStanzaWithAbsoluteFilenames(c *gc.C) {
   616  	dir, err := os.Getwd()
   617  	c.Assert(err, jc.ErrorIsNil)
   618  	fullpath := fmt.Sprintf("%s/testdata/TestInputSourceStanza/interfaces.d/*.cfg", dir)
   619  	content := fmt.Sprintf("source %s", fullpath)
   620  	stanzas, err := debinterfaces.ParseSource("", content, s.expander)
   621  	c.Assert(err, gc.IsNil)
   622  	c.Assert(stanzas, gc.HasLen, 1)
   623  	c.Assert(stanzas[0], gc.FitsTypeOf, debinterfaces.SourceStanza{})
   624  
   625  	c.Assert(stanzas[0].Location().Filename, gc.Equals, "")
   626  	c.Assert(stanzas[0].Location().LineNum, gc.Equals, 1)
   627  
   628  	source := stanzas[0].(debinterfaces.SourceStanza)
   629  	c.Check(source.Path, gc.Equals, fullpath)
   630  
   631  	c.Assert(source.Stanzas, gc.HasLen, 6)
   632  
   633  	c.Assert(source.Stanzas[0], gc.FitsTypeOf, debinterfaces.AutoStanza{})
   634  	c.Assert(source.Stanzas[1], gc.FitsTypeOf, debinterfaces.IfaceStanza{})
   635  	c.Assert(source.Stanzas[2], gc.FitsTypeOf, debinterfaces.AutoStanza{})
   636  	c.Assert(source.Stanzas[3], gc.FitsTypeOf, debinterfaces.IfaceStanza{})
   637  	c.Assert(source.Stanzas[4], gc.FitsTypeOf, debinterfaces.AutoStanza{})
   638  	c.Assert(source.Stanzas[5], gc.FitsTypeOf, debinterfaces.IfaceStanza{})
   639  
   640  	c.Check(source.Sources, gc.DeepEquals, []string{
   641  		filepath.Join(dir, "testdata/TestInputSourceStanza/interfaces.d/eth0.cfg"),
   642  		filepath.Join(dir, "testdata/TestInputSourceStanza/interfaces.d/eth1.cfg"),
   643  		filepath.Join(dir, "testdata/TestInputSourceStanza/interfaces.d/eth2.cfg"),
   644  	})
   645  
   646  	// Note: we don't have tests for stanzas nested > 1 deep.
   647  
   648  	eth0 := source.Stanzas[1].(debinterfaces.IfaceStanza)
   649  	eth1 := source.Stanzas[3].(debinterfaces.IfaceStanza)
   650  	eth2 := source.Stanzas[5].(debinterfaces.IfaceStanza)
   651  
   652  	c.Assert(eth0.Definition(), gc.HasLen, 1)
   653  	c.Check(eth0.Definition()[0], gc.Equals, "iface eth0 inet dhcp")
   654  	c.Check(eth0.Location().Filename, gc.Equals, filepath.Join(dir, "testdata/TestInputSourceStanza/interfaces.d/eth0.cfg"))
   655  	c.Check(eth0.Location().LineNum, gc.Equals, 2)
   656  
   657  	c.Assert(eth1.Definition(), gc.HasLen, 3)
   658  	c.Check(eth1.Definition()[0], gc.Equals, "iface eth1 inet static")
   659  	c.Check(eth1.Definition()[1], gc.Equals, "address 192.168.1.64")
   660  	c.Check(eth1.Definition()[2], gc.Equals, "dns-nameservers 192.168.1.254")
   661  	c.Check(eth1.Location().Filename, gc.Equals, filepath.Join(dir, "testdata/TestInputSourceStanza/interfaces.d/eth1.cfg"))
   662  	c.Check(eth1.Location().LineNum, gc.Equals, 2)
   663  
   664  	c.Assert(eth2.Definition(), gc.HasLen, 1)
   665  	c.Check(eth2.Definition()[0], gc.Equals, "iface eth2 inet manual")
   666  	c.Check(eth2.Location().Filename, gc.Equals, filepath.Join(dir, "testdata/TestInputSourceStanza/interfaces.d/eth2.cfg"))
   667  	c.Check(eth2.Location().LineNum, gc.Equals, 2)
   668  }
   669  
   670  func (s *ParserSuite) TestSourceDirectoryStanzaWithAbsoluteFilenames(c *gc.C) {
   671  	dir, err := os.Getwd()
   672  	c.Assert(err, jc.ErrorIsNil)
   673  	fullpath := fmt.Sprintf("%s/testdata/TestInputSourceDirectoryStanza/interfaces.d", dir)
   674  	content := fmt.Sprintf("source-directory %s", fullpath)
   675  	stanzas, err := debinterfaces.ParseSource("", content, s.expander)
   676  	c.Assert(err, gc.IsNil)
   677  	c.Assert(stanzas, gc.HasLen, 1)
   678  	c.Assert(stanzas[0], gc.FitsTypeOf, debinterfaces.SourceDirectoryStanza{})
   679  
   680  	c.Assert(stanzas[0].Location().Filename, gc.Equals, "")
   681  	c.Assert(stanzas[0].Location().LineNum, gc.Equals, 1)
   682  
   683  	source := stanzas[0].(debinterfaces.SourceDirectoryStanza)
   684  	c.Check(source.Path, gc.Equals, fullpath)
   685  
   686  	c.Assert(source.Stanzas, gc.HasLen, 2)
   687  
   688  	c.Assert(source.Stanzas[0], gc.FitsTypeOf, debinterfaces.AutoStanza{})
   689  	c.Assert(source.Stanzas[1], gc.FitsTypeOf, debinterfaces.IfaceStanza{})
   690  
   691  	c.Check(source.Sources, gc.DeepEquals, []string{
   692  		filepath.Join(fullpath, "eth3"),
   693  	})
   694  
   695  	// Note: we don't have tests for source directory stanzas nested > 1 deep.
   696  
   697  	eth3 := source.Stanzas[1].(debinterfaces.IfaceStanza)
   698  	c.Assert(eth3.Definition(), gc.HasLen, 3)
   699  	c.Check(eth3.Definition()[0], gc.Equals, "iface eth3 inet static")
   700  	c.Check(eth3.Definition()[1], gc.Equals, "address 192.168.1.128")
   701  	c.Check(eth3.Definition()[2], gc.Equals, "dns-nameservers 192.168.1.254")
   702  	c.Check(eth3.Location().Filename, gc.Equals, filepath.Join(fullpath, "eth3"))
   703  	c.Check(eth3.Location().LineNum, gc.Equals, 2)
   704  }
   705  
   706  func (s *ParserSuite) TestSourceStanzaWithAbsoluteNonExistentFilenames(c *gc.C) {
   707  	dir, err := os.Getwd()
   708  	c.Assert(err, jc.ErrorIsNil)
   709  	fullpath := fmt.Sprintf("%s/testdata/non-existent.d/*", dir)
   710  	content := fmt.Sprintf("source %s", fullpath)
   711  	_, err = debinterfaces.ParseSource("", content, s.expander)
   712  	c.Assert(err, gc.IsNil)
   713  }
   714  
   715  func (s *ParserSuite) TestSourceDirectoryStanzaWithAbsoluteNonExistentFilenames(c *gc.C) {
   716  	dir, err := os.Getwd()
   717  	c.Assert(err, jc.ErrorIsNil)
   718  	fullpath := fmt.Sprintf("%s/testdata/non-existent", dir)
   719  	content := fmt.Sprintf("source-directory %s", fullpath)
   720  	_, err = debinterfaces.ParseSource("", content, s.expander)
   721  	c.Assert(err, gc.NotNil)
   722  }
   723  
   724  func (s *ParserSuite) TestIfupdownPackageExample(c *gc.C) {
   725  	_, err := debinterfaces.ParseSource("testdata/ifupdown-examples", nil, s.expander)
   726  	c.Assert(err, gc.IsNil)
   727  }