github.com/hspak/nomad@v0.7.2-0.20180309000617-bc4ae22a39a5/jobspec/parse_test.go (about)

     1  package jobspec
     2  
     3  import (
     4  	"path/filepath"
     5  	"reflect"
     6  	"strings"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/hashicorp/nomad/api"
    11  	"github.com/hashicorp/nomad/helper"
    12  	"github.com/hashicorp/nomad/nomad/structs"
    13  	"github.com/kr/pretty"
    14  
    15  	capi "github.com/hashicorp/consul/api"
    16  )
    17  
    18  func TestParse(t *testing.T) {
    19  	cases := []struct {
    20  		File   string
    21  		Result *api.Job
    22  		Err    bool
    23  	}{
    24  		{
    25  			"basic.hcl",
    26  			&api.Job{
    27  				ID:          helper.StringToPtr("binstore-storagelocker"),
    28  				Name:        helper.StringToPtr("binstore-storagelocker"),
    29  				Type:        helper.StringToPtr("batch"),
    30  				Priority:    helper.IntToPtr(52),
    31  				AllAtOnce:   helper.BoolToPtr(true),
    32  				Datacenters: []string{"us2", "eu1"},
    33  				Region:      helper.StringToPtr("fooregion"),
    34  				Namespace:   helper.StringToPtr("foonamespace"),
    35  				VaultToken:  helper.StringToPtr("foo"),
    36  
    37  				Meta: map[string]string{
    38  					"foo": "bar",
    39  				},
    40  
    41  				Constraints: []*api.Constraint{
    42  					{
    43  						LTarget: "kernel.os",
    44  						RTarget: "windows",
    45  						Operand: "=",
    46  					},
    47  				},
    48  
    49  				Update: &api.UpdateStrategy{
    50  					Stagger:         helper.TimeToPtr(60 * time.Second),
    51  					MaxParallel:     helper.IntToPtr(2),
    52  					HealthCheck:     helper.StringToPtr("manual"),
    53  					MinHealthyTime:  helper.TimeToPtr(10 * time.Second),
    54  					HealthyDeadline: helper.TimeToPtr(10 * time.Minute),
    55  					AutoRevert:      helper.BoolToPtr(true),
    56  					Canary:          helper.IntToPtr(1),
    57  				},
    58  
    59  				TaskGroups: []*api.TaskGroup{
    60  					{
    61  						Name: helper.StringToPtr("outside"),
    62  						Tasks: []*api.Task{
    63  							{
    64  								Name:   "outside",
    65  								Driver: "java",
    66  								Config: map[string]interface{}{
    67  									"jar_path": "s3://my-cool-store/foo.jar",
    68  								},
    69  								Meta: map[string]string{
    70  									"my-cool-key": "foobar",
    71  								},
    72  							},
    73  						},
    74  					},
    75  
    76  					{
    77  						Name:  helper.StringToPtr("binsl"),
    78  						Count: helper.IntToPtr(5),
    79  						Constraints: []*api.Constraint{
    80  							{
    81  								LTarget: "kernel.os",
    82  								RTarget: "linux",
    83  								Operand: "=",
    84  							},
    85  						},
    86  						Meta: map[string]string{
    87  							"elb_mode":     "tcp",
    88  							"elb_interval": "10",
    89  							"elb_checks":   "3",
    90  						},
    91  						RestartPolicy: &api.RestartPolicy{
    92  							Interval: helper.TimeToPtr(10 * time.Minute),
    93  							Attempts: helper.IntToPtr(5),
    94  							Delay:    helper.TimeToPtr(15 * time.Second),
    95  							Mode:     helper.StringToPtr("delay"),
    96  						},
    97  						ReschedulePolicy: &api.ReschedulePolicy{
    98  							Interval: helper.TimeToPtr(12 * time.Hour),
    99  							Attempts: helper.IntToPtr(5),
   100  						},
   101  						EphemeralDisk: &api.EphemeralDisk{
   102  							Sticky: helper.BoolToPtr(true),
   103  							SizeMB: helper.IntToPtr(150),
   104  						},
   105  						Update: &api.UpdateStrategy{
   106  							MaxParallel:     helper.IntToPtr(3),
   107  							HealthCheck:     helper.StringToPtr("checks"),
   108  							MinHealthyTime:  helper.TimeToPtr(1 * time.Second),
   109  							HealthyDeadline: helper.TimeToPtr(1 * time.Minute),
   110  							AutoRevert:      helper.BoolToPtr(false),
   111  							Canary:          helper.IntToPtr(2),
   112  						},
   113  						Tasks: []*api.Task{
   114  							{
   115  								Name:   "binstore",
   116  								Driver: "docker",
   117  								User:   "bob",
   118  								Config: map[string]interface{}{
   119  									"image": "hashicorp/binstore",
   120  									"labels": []map[string]interface{}{
   121  										{
   122  											"FOO": "bar",
   123  										},
   124  									},
   125  								},
   126  								Services: []*api.Service{
   127  									{
   128  										Tags:      []string{"foo", "bar"},
   129  										PortLabel: "http",
   130  										Checks: []api.ServiceCheck{
   131  											{
   132  												Name:      "check-name",
   133  												Type:      "tcp",
   134  												PortLabel: "admin",
   135  												Interval:  10 * time.Second,
   136  												Timeout:   2 * time.Second,
   137  												CheckRestart: &api.CheckRestart{
   138  													Limit:          3,
   139  													Grace:          helper.TimeToPtr(10 * time.Second),
   140  													IgnoreWarnings: true,
   141  												},
   142  											},
   143  										},
   144  									},
   145  								},
   146  								Env: map[string]string{
   147  									"HELLO": "world",
   148  									"LOREM": "ipsum",
   149  								},
   150  								Resources: &api.Resources{
   151  									CPU:      helper.IntToPtr(500),
   152  									MemoryMB: helper.IntToPtr(128),
   153  									Networks: []*api.NetworkResource{
   154  										{
   155  											MBits:         helper.IntToPtr(100),
   156  											ReservedPorts: []api.Port{{Label: "one", Value: 1}, {Label: "two", Value: 2}, {Label: "three", Value: 3}},
   157  											DynamicPorts:  []api.Port{{Label: "http", Value: 0}, {Label: "https", Value: 0}, {Label: "admin", Value: 0}},
   158  										},
   159  									},
   160  								},
   161  								KillTimeout:   helper.TimeToPtr(22 * time.Second),
   162  								ShutdownDelay: 11 * time.Second,
   163  								LogConfig: &api.LogConfig{
   164  									MaxFiles:      helper.IntToPtr(14),
   165  									MaxFileSizeMB: helper.IntToPtr(101),
   166  								},
   167  								Artifacts: []*api.TaskArtifact{
   168  									{
   169  										GetterSource: helper.StringToPtr("http://foo.com/artifact"),
   170  										GetterOptions: map[string]string{
   171  											"checksum": "md5:b8a4f3f72ecab0510a6a31e997461c5f",
   172  										},
   173  									},
   174  									{
   175  										GetterSource: helper.StringToPtr("http://bar.com/artifact"),
   176  										RelativeDest: helper.StringToPtr("test/foo/"),
   177  										GetterOptions: map[string]string{
   178  											"checksum": "md5:ff1cc0d3432dad54d607c1505fb7245c",
   179  										},
   180  										GetterMode: helper.StringToPtr("file"),
   181  									},
   182  								},
   183  								Vault: &api.Vault{
   184  									Policies:   []string{"foo", "bar"},
   185  									Env:        helper.BoolToPtr(true),
   186  									ChangeMode: helper.StringToPtr(structs.VaultChangeModeRestart),
   187  								},
   188  								Templates: []*api.Template{
   189  									{
   190  										SourcePath:   helper.StringToPtr("foo"),
   191  										DestPath:     helper.StringToPtr("foo"),
   192  										ChangeMode:   helper.StringToPtr("foo"),
   193  										ChangeSignal: helper.StringToPtr("foo"),
   194  										Splay:        helper.TimeToPtr(10 * time.Second),
   195  										Perms:        helper.StringToPtr("0644"),
   196  										Envvars:      helper.BoolToPtr(true),
   197  										VaultGrace:   helper.TimeToPtr(33 * time.Second),
   198  									},
   199  									{
   200  										SourcePath: helper.StringToPtr("bar"),
   201  										DestPath:   helper.StringToPtr("bar"),
   202  										ChangeMode: helper.StringToPtr(structs.TemplateChangeModeRestart),
   203  										Splay:      helper.TimeToPtr(5 * time.Second),
   204  										Perms:      helper.StringToPtr("777"),
   205  										LeftDelim:  helper.StringToPtr("--"),
   206  										RightDelim: helper.StringToPtr("__"),
   207  									},
   208  								},
   209  								Leader:     true,
   210  								KillSignal: "",
   211  							},
   212  							{
   213  								Name:   "storagelocker",
   214  								Driver: "docker",
   215  								User:   "",
   216  								Config: map[string]interface{}{
   217  									"image": "hashicorp/storagelocker",
   218  								},
   219  								Resources: &api.Resources{
   220  									CPU:      helper.IntToPtr(500),
   221  									MemoryMB: helper.IntToPtr(128),
   222  									IOPS:     helper.IntToPtr(30),
   223  								},
   224  								Constraints: []*api.Constraint{
   225  									{
   226  										LTarget: "kernel.arch",
   227  										RTarget: "amd64",
   228  										Operand: "=",
   229  									},
   230  								},
   231  								Vault: &api.Vault{
   232  									Policies:     []string{"foo", "bar"},
   233  									Env:          helper.BoolToPtr(false),
   234  									ChangeMode:   helper.StringToPtr(structs.VaultChangeModeSignal),
   235  									ChangeSignal: helper.StringToPtr("SIGUSR1"),
   236  								},
   237  							},
   238  						},
   239  					},
   240  				},
   241  			},
   242  			false,
   243  		},
   244  
   245  		{
   246  			"multi-network.hcl",
   247  			nil,
   248  			true,
   249  		},
   250  
   251  		{
   252  			"multi-resource.hcl",
   253  			nil,
   254  			true,
   255  		},
   256  
   257  		{
   258  			"multi-vault.hcl",
   259  			nil,
   260  			true,
   261  		},
   262  
   263  		{
   264  			"default-job.hcl",
   265  			&api.Job{
   266  				ID:   helper.StringToPtr("foo"),
   267  				Name: helper.StringToPtr("foo"),
   268  			},
   269  			false,
   270  		},
   271  
   272  		{
   273  			"version-constraint.hcl",
   274  			&api.Job{
   275  				ID:   helper.StringToPtr("foo"),
   276  				Name: helper.StringToPtr("foo"),
   277  				Constraints: []*api.Constraint{
   278  					{
   279  						LTarget: "$attr.kernel.version",
   280  						RTarget: "~> 3.2",
   281  						Operand: structs.ConstraintVersion,
   282  					},
   283  				},
   284  			},
   285  			false,
   286  		},
   287  
   288  		{
   289  			"regexp-constraint.hcl",
   290  			&api.Job{
   291  				ID:   helper.StringToPtr("foo"),
   292  				Name: helper.StringToPtr("foo"),
   293  				Constraints: []*api.Constraint{
   294  					{
   295  						LTarget: "$attr.kernel.version",
   296  						RTarget: "[0-9.]+",
   297  						Operand: structs.ConstraintRegex,
   298  					},
   299  				},
   300  			},
   301  			false,
   302  		},
   303  
   304  		{
   305  			"set-contains-constraint.hcl",
   306  			&api.Job{
   307  				ID:   helper.StringToPtr("foo"),
   308  				Name: helper.StringToPtr("foo"),
   309  				Constraints: []*api.Constraint{
   310  					{
   311  						LTarget: "$meta.data",
   312  						RTarget: "foo,bar,baz",
   313  						Operand: structs.ConstraintSetContains,
   314  					},
   315  				},
   316  			},
   317  			false,
   318  		},
   319  
   320  		{
   321  			"distinctHosts-constraint.hcl",
   322  			&api.Job{
   323  				ID:   helper.StringToPtr("foo"),
   324  				Name: helper.StringToPtr("foo"),
   325  				Constraints: []*api.Constraint{
   326  					{
   327  						Operand: structs.ConstraintDistinctHosts,
   328  					},
   329  				},
   330  			},
   331  			false,
   332  		},
   333  
   334  		{
   335  			"distinctProperty-constraint.hcl",
   336  			&api.Job{
   337  				ID:   helper.StringToPtr("foo"),
   338  				Name: helper.StringToPtr("foo"),
   339  				Constraints: []*api.Constraint{
   340  					{
   341  						Operand: structs.ConstraintDistinctProperty,
   342  						LTarget: "${meta.rack}",
   343  					},
   344  				},
   345  			},
   346  			false,
   347  		},
   348  
   349  		{
   350  			"periodic-cron.hcl",
   351  			&api.Job{
   352  				ID:   helper.StringToPtr("foo"),
   353  				Name: helper.StringToPtr("foo"),
   354  				Periodic: &api.PeriodicConfig{
   355  					SpecType:        helper.StringToPtr(api.PeriodicSpecCron),
   356  					Spec:            helper.StringToPtr("*/5 * * *"),
   357  					ProhibitOverlap: helper.BoolToPtr(true),
   358  					TimeZone:        helper.StringToPtr("Europe/Minsk"),
   359  				},
   360  			},
   361  			false,
   362  		},
   363  
   364  		{
   365  			"specify-job.hcl",
   366  			&api.Job{
   367  				ID:   helper.StringToPtr("job1"),
   368  				Name: helper.StringToPtr("My Job"),
   369  			},
   370  			false,
   371  		},
   372  
   373  		{
   374  			"task-nested-config.hcl",
   375  			&api.Job{
   376  				ID:   helper.StringToPtr("foo"),
   377  				Name: helper.StringToPtr("foo"),
   378  				TaskGroups: []*api.TaskGroup{
   379  					{
   380  						Name: helper.StringToPtr("bar"),
   381  						Tasks: []*api.Task{
   382  							{
   383  								Name:   "bar",
   384  								Driver: "docker",
   385  								Config: map[string]interface{}{
   386  									"image": "hashicorp/image",
   387  									"port_map": []map[string]interface{}{
   388  										{
   389  											"db": 1234,
   390  										},
   391  									},
   392  								},
   393  							},
   394  						},
   395  					},
   396  				},
   397  			},
   398  			false,
   399  		},
   400  
   401  		{
   402  			"bad-artifact.hcl",
   403  			nil,
   404  			true,
   405  		},
   406  
   407  		{
   408  			"artifacts.hcl",
   409  			&api.Job{
   410  				ID:   helper.StringToPtr("binstore-storagelocker"),
   411  				Name: helper.StringToPtr("binstore-storagelocker"),
   412  				TaskGroups: []*api.TaskGroup{
   413  					{
   414  						Name: helper.StringToPtr("binsl"),
   415  						Tasks: []*api.Task{
   416  							{
   417  								Name:   "binstore",
   418  								Driver: "docker",
   419  								Artifacts: []*api.TaskArtifact{
   420  									{
   421  										GetterSource:  helper.StringToPtr("http://foo.com/bar"),
   422  										GetterOptions: map[string]string{"foo": "bar"},
   423  										RelativeDest:  helper.StringToPtr(""),
   424  									},
   425  									{
   426  										GetterSource:  helper.StringToPtr("http://foo.com/baz"),
   427  										GetterOptions: nil,
   428  										RelativeDest:  nil,
   429  									},
   430  									{
   431  										GetterSource:  helper.StringToPtr("http://foo.com/bam"),
   432  										GetterOptions: nil,
   433  										RelativeDest:  helper.StringToPtr("var/foo"),
   434  									},
   435  								},
   436  							},
   437  						},
   438  					},
   439  				},
   440  			},
   441  			false,
   442  		},
   443  		{
   444  			"service-check-initial-status.hcl",
   445  			&api.Job{
   446  				ID:   helper.StringToPtr("check_initial_status"),
   447  				Name: helper.StringToPtr("check_initial_status"),
   448  				Type: helper.StringToPtr("service"),
   449  				TaskGroups: []*api.TaskGroup{
   450  					{
   451  						Name:  helper.StringToPtr("group"),
   452  						Count: helper.IntToPtr(1),
   453  						Tasks: []*api.Task{
   454  							{
   455  								Name: "task",
   456  								Services: []*api.Service{
   457  									{
   458  										Tags:      []string{"foo", "bar"},
   459  										PortLabel: "http",
   460  										Checks: []api.ServiceCheck{
   461  											{
   462  												Name:          "check-name",
   463  												Type:          "http",
   464  												Path:          "/",
   465  												Interval:      10 * time.Second,
   466  												Timeout:       2 * time.Second,
   467  												InitialStatus: capi.HealthPassing,
   468  												Method:        "POST",
   469  												Header: map[string][]string{
   470  													"Authorization": {"Basic ZWxhc3RpYzpjaGFuZ2VtZQ=="},
   471  												},
   472  											},
   473  										},
   474  									},
   475  								},
   476  							},
   477  						},
   478  					},
   479  				},
   480  			},
   481  			false,
   482  		},
   483  		{
   484  			"service-check-bad-header.hcl",
   485  			nil,
   486  			true,
   487  		},
   488  		{
   489  			"service-check-bad-header-2.hcl",
   490  			nil,
   491  			true,
   492  		},
   493  		{
   494  			// TODO This should be pushed into the API
   495  			"vault_inheritance.hcl",
   496  			&api.Job{
   497  				ID:   helper.StringToPtr("example"),
   498  				Name: helper.StringToPtr("example"),
   499  				TaskGroups: []*api.TaskGroup{
   500  					{
   501  						Name: helper.StringToPtr("cache"),
   502  						Tasks: []*api.Task{
   503  							{
   504  								Name: "redis",
   505  								Vault: &api.Vault{
   506  									Policies:   []string{"group"},
   507  									Env:        helper.BoolToPtr(true),
   508  									ChangeMode: helper.StringToPtr(structs.VaultChangeModeRestart),
   509  								},
   510  							},
   511  							{
   512  								Name: "redis2",
   513  								Vault: &api.Vault{
   514  									Policies:   []string{"task"},
   515  									Env:        helper.BoolToPtr(false),
   516  									ChangeMode: helper.StringToPtr(structs.VaultChangeModeRestart),
   517  								},
   518  							},
   519  						},
   520  					},
   521  					{
   522  						Name: helper.StringToPtr("cache2"),
   523  						Tasks: []*api.Task{
   524  							{
   525  								Name: "redis",
   526  								Vault: &api.Vault{
   527  									Policies:   []string{"job"},
   528  									Env:        helper.BoolToPtr(true),
   529  									ChangeMode: helper.StringToPtr(structs.VaultChangeModeRestart),
   530  								},
   531  							},
   532  						},
   533  					},
   534  				},
   535  			},
   536  			false,
   537  		},
   538  		{
   539  			"parameterized_job.hcl",
   540  			&api.Job{
   541  				ID:   helper.StringToPtr("parameterized_job"),
   542  				Name: helper.StringToPtr("parameterized_job"),
   543  
   544  				ParameterizedJob: &api.ParameterizedJobConfig{
   545  					Payload:      "required",
   546  					MetaRequired: []string{"foo", "bar"},
   547  					MetaOptional: []string{"baz", "bam"},
   548  				},
   549  
   550  				TaskGroups: []*api.TaskGroup{
   551  					{
   552  						Name: helper.StringToPtr("foo"),
   553  						Tasks: []*api.Task{
   554  							{
   555  								Name:   "bar",
   556  								Driver: "docker",
   557  								DispatchPayload: &api.DispatchPayloadConfig{
   558  									File: "foo/bar",
   559  								},
   560  							},
   561  						},
   562  					},
   563  				},
   564  			},
   565  			false,
   566  		},
   567  		{
   568  			"job-with-kill-signal.hcl",
   569  			&api.Job{
   570  				ID:   helper.StringToPtr("foo"),
   571  				Name: helper.StringToPtr("foo"),
   572  				TaskGroups: []*api.TaskGroup{
   573  					{
   574  						Name: helper.StringToPtr("bar"),
   575  						Tasks: []*api.Task{
   576  							{
   577  								Name:       "bar",
   578  								Driver:     "docker",
   579  								KillSignal: "SIGQUIT",
   580  								Config: map[string]interface{}{
   581  									"image": "hashicorp/image",
   582  								},
   583  							},
   584  						},
   585  					},
   586  				},
   587  			},
   588  			false,
   589  		},
   590  		{
   591  			"service-check-driver-address.hcl",
   592  			&api.Job{
   593  				ID:   helper.StringToPtr("address_mode_driver"),
   594  				Name: helper.StringToPtr("address_mode_driver"),
   595  				Type: helper.StringToPtr("service"),
   596  				TaskGroups: []*api.TaskGroup{
   597  					{
   598  						Name: helper.StringToPtr("group"),
   599  						Tasks: []*api.Task{
   600  							{
   601  								Name: "task",
   602  								Services: []*api.Service{
   603  									{
   604  										Name:        "http-service",
   605  										PortLabel:   "http",
   606  										AddressMode: "auto",
   607  										Checks: []api.ServiceCheck{
   608  											{
   609  												Name:        "http-check",
   610  												Type:        "http",
   611  												Path:        "/",
   612  												PortLabel:   "http",
   613  												AddressMode: "driver",
   614  											},
   615  										},
   616  									},
   617  									{
   618  										Name:        "random-service",
   619  										PortLabel:   "9000",
   620  										AddressMode: "driver",
   621  										Checks: []api.ServiceCheck{
   622  											{
   623  												Name:        "random-check",
   624  												Type:        "tcp",
   625  												PortLabel:   "9001",
   626  												AddressMode: "driver",
   627  											},
   628  										},
   629  									},
   630  								},
   631  							},
   632  						},
   633  					},
   634  				},
   635  			},
   636  			false,
   637  		},
   638  		{
   639  			"service-check-restart.hcl",
   640  			&api.Job{
   641  				ID:   helper.StringToPtr("service_check_restart"),
   642  				Name: helper.StringToPtr("service_check_restart"),
   643  				Type: helper.StringToPtr("service"),
   644  				TaskGroups: []*api.TaskGroup{
   645  					{
   646  						Name: helper.StringToPtr("group"),
   647  						Tasks: []*api.Task{
   648  							{
   649  								Name: "task",
   650  								Services: []*api.Service{
   651  									{
   652  										Name: "http-service",
   653  										CheckRestart: &api.CheckRestart{
   654  											Limit:          3,
   655  											Grace:          helper.TimeToPtr(10 * time.Second),
   656  											IgnoreWarnings: true,
   657  										},
   658  										Checks: []api.ServiceCheck{
   659  											{
   660  												Name:      "random-check",
   661  												Type:      "tcp",
   662  												PortLabel: "9001",
   663  											},
   664  										},
   665  									},
   666  								},
   667  							},
   668  						},
   669  					},
   670  				},
   671  			},
   672  			false,
   673  		},
   674  		{
   675  			"reschedule-job.hcl",
   676  			&api.Job{
   677  				ID:          helper.StringToPtr("foo"),
   678  				Name:        helper.StringToPtr("foo"),
   679  				Type:        helper.StringToPtr("batch"),
   680  				Datacenters: []string{"dc1"},
   681  				Reschedule: &api.ReschedulePolicy{
   682  					Attempts: helper.IntToPtr(15),
   683  					Interval: helper.TimeToPtr(30 * time.Minute),
   684  				},
   685  				TaskGroups: []*api.TaskGroup{
   686  					{
   687  						Name:  helper.StringToPtr("bar"),
   688  						Count: helper.IntToPtr(3),
   689  						Tasks: []*api.Task{
   690  							{
   691  								Name:   "bar",
   692  								Driver: "raw_exec",
   693  								Config: map[string]interface{}{
   694  									"command": "bash",
   695  									"args":    []interface{}{"-c", "echo hi"},
   696  								},
   697  							},
   698  						},
   699  					},
   700  				},
   701  			},
   702  			false,
   703  		},
   704  	}
   705  
   706  	for _, tc := range cases {
   707  		t.Logf("Testing parse: %s", tc.File)
   708  
   709  		path, err := filepath.Abs(filepath.Join("./test-fixtures", tc.File))
   710  		if err != nil {
   711  			t.Fatalf("file: %s\n\n%s", tc.File, err)
   712  			continue
   713  		}
   714  
   715  		actual, err := ParseFile(path)
   716  		if (err != nil) != tc.Err {
   717  			t.Fatalf("file: %s\n\n%s", tc.File, err)
   718  			continue
   719  		}
   720  
   721  		if !reflect.DeepEqual(actual, tc.Result) {
   722  			for _, d := range pretty.Diff(actual, tc.Result) {
   723  				t.Logf(d)
   724  			}
   725  			t.Fatalf("file: %s", tc.File)
   726  		}
   727  	}
   728  }
   729  
   730  func TestBadPorts(t *testing.T) {
   731  	path, err := filepath.Abs(filepath.Join("./test-fixtures", "bad-ports.hcl"))
   732  	if err != nil {
   733  		t.Fatalf("Can't get absolute path for file: %s", err)
   734  	}
   735  
   736  	_, err = ParseFile(path)
   737  
   738  	if !strings.Contains(err.Error(), errPortLabel.Error()) {
   739  		t.Fatalf("\nExpected error\n  %s\ngot\n  %v", errPortLabel, err)
   740  	}
   741  }
   742  
   743  func TestOverlappingPorts(t *testing.T) {
   744  	path, err := filepath.Abs(filepath.Join("./test-fixtures", "overlapping-ports.hcl"))
   745  	if err != nil {
   746  		t.Fatalf("Can't get absolute path for file: %s", err)
   747  	}
   748  
   749  	_, err = ParseFile(path)
   750  
   751  	if err == nil {
   752  		t.Fatalf("Expected an error")
   753  	}
   754  
   755  	if !strings.Contains(err.Error(), "found a port label collision") {
   756  		t.Fatalf("Expected collision error; got %v", err)
   757  	}
   758  }
   759  
   760  func TestIncorrectKey(t *testing.T) {
   761  	path, err := filepath.Abs(filepath.Join("./test-fixtures", "basic_wrong_key.hcl"))
   762  	if err != nil {
   763  		t.Fatalf("Can't get absolute path for file: %s", err)
   764  	}
   765  
   766  	_, err = ParseFile(path)
   767  
   768  	if err == nil {
   769  		t.Fatalf("Expected an error")
   770  	}
   771  
   772  	if !strings.Contains(err.Error(), "* group: 'binsl', task: 'binstore', service: 'foo', check -> invalid key: nterval") {
   773  		t.Fatalf("Expected key error; got %v", err)
   774  	}
   775  }