github.com/paketo-buildpacks/packit@v1.3.2-0.20211206231111-86b75c657449/servicebindings/resolver_test.go (about)

     1  package servicebindings_test
     2  
     3  import (
     4  	"os"
     5  	"path/filepath"
     6  	"testing"
     7  
     8  	. "github.com/onsi/gomega"
     9  	"github.com/sclevine/spec"
    10  
    11  	"github.com/paketo-buildpacks/packit/servicebindings"
    12  )
    13  
    14  func testResolver(t *testing.T, context spec.G, it spec.S) {
    15  	var Expect = NewWithT(t).Expect
    16  
    17  	context("binding root precedence", func() {
    18  		var (
    19  			bindingRootK8s string
    20  			bindingRootCNB string
    21  			platformDir    string
    22  		)
    23  
    24  		it.Before(func() {
    25  			var err error
    26  
    27  			bindingRootK8s, err = os.MkdirTemp("", "bindings-k8s")
    28  			Expect(err).NotTo(HaveOccurred())
    29  
    30  			err = os.MkdirAll(filepath.Join(bindingRootK8s, "some-binding"), os.ModePerm)
    31  			Expect(err).NotTo(HaveOccurred())
    32  
    33  			err = os.WriteFile(filepath.Join(bindingRootK8s, "some-binding", "type"), []byte("some-type"), os.ModePerm)
    34  			Expect(err).NotTo(HaveOccurred())
    35  
    36  			bindingRootCNB, err = os.MkdirTemp("", "bindings-cnb")
    37  			Expect(err).NotTo(HaveOccurred())
    38  
    39  			err = os.MkdirAll(filepath.Join(bindingRootCNB, "some-binding"), os.ModePerm)
    40  			Expect(err).NotTo(HaveOccurred())
    41  
    42  			err = os.WriteFile(filepath.Join(bindingRootCNB, "some-binding", "type"), []byte("some-type"), os.ModePerm)
    43  			Expect(err).NotTo(HaveOccurred())
    44  
    45  			platformDir, err = os.MkdirTemp("", "bindings-platform")
    46  			Expect(err).NotTo(HaveOccurred())
    47  
    48  			err = os.MkdirAll(filepath.Join(platformDir, "bindings", "some-binding"), os.ModePerm)
    49  			Expect(err).NotTo(HaveOccurred())
    50  
    51  			err = os.WriteFile(filepath.Join(platformDir, "bindings", "some-binding", "type"), []byte("some-type"), os.ModePerm)
    52  			Expect(err).NotTo(HaveOccurred())
    53  		})
    54  
    55  		context("SERVICE_BINDING_ROOT env var is set", func() {
    56  			it.Before(func() {
    57  				Expect(os.Setenv("SERVICE_BINDING_ROOT", bindingRootK8s)).To(Succeed())
    58  			})
    59  
    60  			context("CNB_BINDINGS env var is set", func() {
    61  				it.Before(func() {
    62  					Expect(os.Setenv("CNB_BINDINGS", bindingRootCNB)).To(Succeed())
    63  				})
    64  
    65  				it("resolves bindings from SERVICE_BINDING_ROOT", func() {
    66  					resolver := servicebindings.NewResolver()
    67  
    68  					bindings, err := resolver.Resolve("some-type", "", platformDir)
    69  					Expect(err).NotTo(HaveOccurred())
    70  					Expect(bindings).To(ConsistOf(
    71  						servicebindings.Binding{
    72  							Name:    "some-binding",
    73  							Path:    filepath.Join(bindingRootK8s, "some-binding"),
    74  							Type:    "some-type",
    75  							Entries: map[string]*servicebindings.Entry{},
    76  						},
    77  					))
    78  				})
    79  			})
    80  
    81  			context("CNB_BINDINGS env var is not set", func() {
    82  				it.Before(func() {
    83  					Expect(os.Unsetenv("CNB_BINDINGS")).To(Succeed())
    84  				})
    85  
    86  				it("resolves bindings from SERVICE_BINDING_ROOT", func() {
    87  					resolver := servicebindings.NewResolver()
    88  
    89  					bindings, err := resolver.Resolve("some-type", "", platformDir)
    90  					Expect(err).NotTo(HaveOccurred())
    91  					Expect(bindings).To(ConsistOf(
    92  						servicebindings.Binding{
    93  							Name:    "some-binding",
    94  							Path:    filepath.Join(bindingRootK8s, "some-binding"),
    95  							Type:    "some-type",
    96  							Entries: map[string]*servicebindings.Entry{},
    97  						},
    98  					))
    99  				})
   100  			})
   101  		})
   102  
   103  		context("SERVICE_BINDING_ROOT env var is not set", func() {
   104  			it.Before(func() {
   105  				Expect(os.Unsetenv("SERVICE_BINDING_ROOT")).To(Succeed())
   106  			})
   107  
   108  			context("CNB_BINDINGS env var is set", func() {
   109  				it.Before(func() {
   110  					Expect(os.Setenv("CNB_BINDINGS", bindingRootCNB)).To(Succeed())
   111  				})
   112  
   113  				it("resolves bindings from CNB_BINDINGS", func() {
   114  					resolver := servicebindings.NewResolver()
   115  
   116  					bindings, err := resolver.Resolve("some-type", "", platformDir)
   117  					Expect(err).NotTo(HaveOccurred())
   118  					Expect(bindings).To(ConsistOf(
   119  						servicebindings.Binding{
   120  							Name:    "some-binding",
   121  							Path:    filepath.Join(bindingRootCNB, "some-binding"),
   122  							Type:    "some-type",
   123  							Entries: map[string]*servicebindings.Entry{},
   124  						},
   125  					))
   126  				})
   127  			})
   128  
   129  			context("CNB_BINDINGS env var is not set", func() {
   130  				it.Before(func() {
   131  					Expect(os.Unsetenv("CNB_BINDINGS")).To(Succeed())
   132  				})
   133  
   134  				it("resolves bindings from platform dir", func() {
   135  					resolver := servicebindings.NewResolver()
   136  
   137  					bindings, err := resolver.Resolve("some-type", "", platformDir)
   138  					Expect(err).NotTo(HaveOccurred())
   139  					Expect(bindings).To(ConsistOf(
   140  						servicebindings.Binding{
   141  							Name:    "some-binding",
   142  							Path:    filepath.Join(platformDir, "bindings", "some-binding"),
   143  							Type:    "some-type",
   144  							Entries: map[string]*servicebindings.Entry{},
   145  						},
   146  					))
   147  				})
   148  			})
   149  		})
   150  	})
   151  
   152  	context("resolving bindings", func() {
   153  		var bindingRoot string
   154  		var resolver *servicebindings.Resolver
   155  
   156  		it.Before(func() {
   157  			var err error
   158  			bindingRoot, err = os.MkdirTemp("", "bindings")
   159  			Expect(err).NotTo(HaveOccurred())
   160  			Expect(os.Setenv("SERVICE_BINDING_ROOT", bindingRoot)).To(Succeed())
   161  
   162  			resolver = servicebindings.NewResolver()
   163  
   164  			err = os.MkdirAll(filepath.Join(bindingRoot, "binding-1A"), os.ModePerm)
   165  			Expect(err).NotTo(HaveOccurred())
   166  
   167  			err = os.WriteFile(filepath.Join(bindingRoot, "binding-1A", "type"), []byte("type-1"), os.ModePerm)
   168  			Expect(err).NotTo(HaveOccurred())
   169  
   170  			err = os.WriteFile(filepath.Join(bindingRoot, "binding-1A", "provider"), []byte("provider-1A"), os.ModePerm)
   171  			Expect(err).NotTo(HaveOccurred())
   172  
   173  			err = os.WriteFile(filepath.Join(bindingRoot, "binding-1A", "username"), nil, os.ModePerm)
   174  			Expect(err).NotTo(HaveOccurred())
   175  
   176  			err = os.WriteFile(filepath.Join(bindingRoot, "binding-1A", "password"), nil, os.ModePerm)
   177  			Expect(err).NotTo(HaveOccurred())
   178  
   179  			err = os.MkdirAll(filepath.Join(bindingRoot, "binding-1B"), os.ModePerm)
   180  			Expect(err).NotTo(HaveOccurred())
   181  
   182  			err = os.WriteFile(filepath.Join(bindingRoot, "binding-1B", "type"), []byte("type-1"), os.ModePerm)
   183  			Expect(err).NotTo(HaveOccurred())
   184  
   185  			err = os.WriteFile(filepath.Join(bindingRoot, "binding-1B", "provider"), []byte("provider-1B"), os.ModePerm)
   186  			Expect(err).NotTo(HaveOccurred())
   187  
   188  			err = os.WriteFile(filepath.Join(bindingRoot, "binding-1B", "username"), nil, os.ModePerm)
   189  			Expect(err).NotTo(HaveOccurred())
   190  
   191  			err = os.WriteFile(filepath.Join(bindingRoot, "binding-1B", "password"), nil, os.ModePerm)
   192  			Expect(err).NotTo(HaveOccurred())
   193  
   194  			err = os.MkdirAll(filepath.Join(bindingRoot, "binding-2"), os.ModePerm)
   195  			Expect(err).NotTo(HaveOccurred())
   196  
   197  			err = os.WriteFile(filepath.Join(bindingRoot, "binding-2", "type"), []byte("type-2"), os.ModePerm)
   198  			Expect(err).NotTo(HaveOccurred())
   199  
   200  			err = os.WriteFile(filepath.Join(bindingRoot, "binding-2", "provider"), []byte("provider-2"), os.ModePerm)
   201  			Expect(err).NotTo(HaveOccurred())
   202  
   203  			err = os.WriteFile(filepath.Join(bindingRoot, "binding-2", "username"), nil, os.ModePerm)
   204  			Expect(err).NotTo(HaveOccurred())
   205  
   206  			err = os.WriteFile(filepath.Join(bindingRoot, "binding-2", "password"), nil, os.ModePerm)
   207  			Expect(err).NotTo(HaveOccurred())
   208  		})
   209  
   210  		it.After(func() {
   211  			Expect(os.RemoveAll(bindingRoot)).To(Succeed())
   212  			Expect(os.Unsetenv("SERVICE_BINDING_ROOT")).To(Succeed())
   213  		})
   214  
   215  		context("Resolve", func() {
   216  			it("resolves by type only (case-insensitive)", func() {
   217  				bindings, err := resolver.Resolve("TyPe-1", "", "")
   218  				Expect(err).NotTo(HaveOccurred())
   219  
   220  				Expect(bindings).To(ConsistOf(
   221  					servicebindings.Binding{
   222  						Name:     "binding-1A",
   223  						Path:     filepath.Join(bindingRoot, "binding-1A"),
   224  						Type:     "type-1",
   225  						Provider: "provider-1A",
   226  						Entries: map[string]*servicebindings.Entry{
   227  							"username": servicebindings.NewEntry(filepath.Join(bindingRoot, "binding-1A", "username")),
   228  							"password": servicebindings.NewEntry(filepath.Join(bindingRoot, "binding-1A", "password")),
   229  						},
   230  					},
   231  					servicebindings.Binding{
   232  						Name:     "binding-1B",
   233  						Path:     filepath.Join(bindingRoot, "binding-1B"),
   234  						Type:     "type-1",
   235  						Provider: "provider-1B",
   236  						Entries: map[string]*servicebindings.Entry{
   237  							"username": servicebindings.NewEntry(filepath.Join(bindingRoot, "binding-1B", "username")),
   238  							"password": servicebindings.NewEntry(filepath.Join(bindingRoot, "binding-1B", "password")),
   239  						},
   240  					},
   241  				))
   242  			})
   243  
   244  			it("resolves by type and provider (case-insensitive)", func() {
   245  				bindings, err := resolver.Resolve("TyPe-1", "PrOvIdEr-1B", "")
   246  				Expect(err).NotTo(HaveOccurred())
   247  
   248  				Expect(bindings).To(ConsistOf(
   249  					servicebindings.Binding{
   250  						Name:     "binding-1B",
   251  						Path:     filepath.Join(bindingRoot, "binding-1B"),
   252  						Type:     "type-1",
   253  						Provider: "provider-1B",
   254  						Entries: map[string]*servicebindings.Entry{
   255  							"username": servicebindings.NewEntry(filepath.Join(bindingRoot, "binding-1B", "username")),
   256  							"password": servicebindings.NewEntry(filepath.Join(bindingRoot, "binding-1B", "password")),
   257  						},
   258  					},
   259  				))
   260  			})
   261  
   262  			it("allows 'metadata' as an entry name", func() {
   263  				err := os.MkdirAll(filepath.Join(bindingRoot, "binding-metadata"), os.ModePerm)
   264  				Expect(err).NotTo(HaveOccurred())
   265  
   266  				err = os.WriteFile(filepath.Join(bindingRoot, "binding-metadata", "type"), []byte("type-metadata"), os.ModePerm)
   267  				Expect(err).NotTo(HaveOccurred())
   268  
   269  				err = os.WriteFile(filepath.Join(bindingRoot, "binding-metadata", "metadata"), nil, os.ModePerm)
   270  				Expect(err).NotTo(HaveOccurred())
   271  
   272  				bindings, err := resolver.Resolve("type-metadata", "", "")
   273  				Expect(err).NotTo(HaveOccurred())
   274  
   275  				Expect(bindings).To(ConsistOf(
   276  					servicebindings.Binding{
   277  						Name: "binding-metadata",
   278  						Path: filepath.Join(bindingRoot, "binding-metadata"),
   279  						Type: "type-metadata",
   280  						Entries: map[string]*servicebindings.Entry{
   281  							"metadata": servicebindings.NewEntry(filepath.Join(bindingRoot, "binding-metadata", "metadata")),
   282  						},
   283  					},
   284  				))
   285  			})
   286  
   287  			it("returns an error if type is missing", func() {
   288  				err := os.MkdirAll(filepath.Join(bindingRoot, "bad-binding"), os.ModePerm)
   289  				Expect(err).NotTo(HaveOccurred())
   290  
   291  				_, err = resolver.Resolve("bad-type", "", "")
   292  				Expect(err).To(MatchError(HavePrefix("failed to load bindings from '%s': failed to read binding 'bad-binding': missing 'type'", bindingRoot)))
   293  			})
   294  
   295  			it("allows provider to be omitted", func() {
   296  				err := os.MkdirAll(filepath.Join(bindingRoot, "some-binding"), os.ModePerm)
   297  				Expect(err).NotTo(HaveOccurred())
   298  
   299  				err = os.WriteFile(filepath.Join(bindingRoot, "some-binding", "type"), []byte("some-type"), os.ModePerm)
   300  				Expect(err).NotTo(HaveOccurred())
   301  
   302  				bindings, err := resolver.Resolve("some-type", "", "")
   303  				Expect(err).NotTo(HaveOccurred())
   304  
   305  				Expect(bindings).To(ConsistOf(
   306  					servicebindings.Binding{
   307  						Name:     "some-binding",
   308  						Path:     filepath.Join(bindingRoot, "some-binding"),
   309  						Type:     "some-type",
   310  						Provider: "",
   311  						Entries:  map[string]*servicebindings.Entry{},
   312  					},
   313  				))
   314  			})
   315  
   316  			it("returns errors encountered reading files", func() {
   317  				err := os.MkdirAll(filepath.Join(bindingRoot, "bad-binding"), os.ModePerm)
   318  				Expect(err).NotTo(HaveOccurred())
   319  
   320  				err = os.WriteFile(filepath.Join(bindingRoot, "bad-binding", "type"), []byte("bad-type"), 000)
   321  				Expect(err).NotTo(HaveOccurred())
   322  
   323  				_, err = resolver.Resolve("bad-type", "", "")
   324  				Expect(err).To(MatchError(HavePrefix("failed to load bindings from '%s': failed to read binding 'bad-binding': open %s: permission denied", bindingRoot, filepath.Join(bindingRoot, "bad-binding", "type"))))
   325  			})
   326  
   327  			it("returns empty list if binding root doesn't exist", func() {
   328  				Expect(os.RemoveAll(bindingRoot)).To(Succeed())
   329  
   330  				bindings, err := resolver.Resolve("type-1", "", "")
   331  				Expect(err).NotTo(HaveOccurred())
   332  				Expect(bindings).To(BeEmpty())
   333  			})
   334  		})
   335  
   336  		context("ResolveOne", func() {
   337  			it("resolves one binding (case-insensitive)", func() {
   338  				binding, err := resolver.ResolveOne("TyPe-2", "", "")
   339  				Expect(err).NotTo(HaveOccurred())
   340  				Expect(binding).To(Equal(servicebindings.Binding{
   341  					Name:     "binding-2",
   342  					Path:     filepath.Join(bindingRoot, "binding-2"),
   343  					Type:     "type-2",
   344  					Provider: "provider-2",
   345  					Entries: map[string]*servicebindings.Entry{
   346  						"username": servicebindings.NewEntry(filepath.Join(bindingRoot, "binding-2", "username")),
   347  						"password": servicebindings.NewEntry(filepath.Join(bindingRoot, "binding-2", "password")),
   348  					},
   349  				}))
   350  			})
   351  
   352  			it("returns an error if no matches", func() {
   353  				_, err := resolver.ResolveOne("non-existent-type", "non-existent-provider", "")
   354  				Expect(err).To(MatchError("found 0 bindings for type 'non-existent-type' and provider 'non-existent-provider' but expected exactly 1"))
   355  			})
   356  
   357  			it("returns an error if more than one match", func() {
   358  				_, err := resolver.ResolveOne("TyPe-1", "", "")
   359  				Expect(err).To(MatchError("found 2 bindings for type 'TyPe-1' and provider '' but expected exactly 1"))
   360  			})
   361  		})
   362  
   363  		context("legacy bindings", func() {
   364  			it("resolves legacy bindings", func() {
   365  				err := os.MkdirAll(filepath.Join(bindingRoot, "binding-legacy", "metadata"), os.ModePerm)
   366  				Expect(err).NotTo(HaveOccurred())
   367  
   368  				err = os.MkdirAll(filepath.Join(bindingRoot, "binding-legacy", "secret"), os.ModePerm)
   369  				Expect(err).NotTo(HaveOccurred())
   370  
   371  				err = os.WriteFile(filepath.Join(bindingRoot, "binding-legacy", "metadata", "kind"), []byte("type-legacy"), os.ModePerm)
   372  				Expect(err).NotTo(HaveOccurred())
   373  
   374  				err = os.WriteFile(filepath.Join(bindingRoot, "binding-legacy", "metadata", "provider"), []byte("provider-legacy"), os.ModePerm)
   375  				Expect(err).NotTo(HaveOccurred())
   376  
   377  				err = os.WriteFile(filepath.Join(bindingRoot, "binding-legacy", "metadata", "username"), nil, os.ModePerm)
   378  				Expect(err).NotTo(HaveOccurred())
   379  
   380  				err = os.WriteFile(filepath.Join(bindingRoot, "binding-legacy", "secret", "password"), nil, os.ModePerm)
   381  				Expect(err).NotTo(HaveOccurred())
   382  
   383  				bindings, err := resolver.Resolve("type-legacy", "", "")
   384  				Expect(err).NotTo(HaveOccurred())
   385  
   386  				Expect(bindings).To(ConsistOf(
   387  					servicebindings.Binding{
   388  						Name:     "binding-legacy",
   389  						Path:     filepath.Join(bindingRoot, "binding-legacy"),
   390  						Type:     "type-legacy",
   391  						Provider: "provider-legacy",
   392  						Entries: map[string]*servicebindings.Entry{
   393  							"username": servicebindings.NewEntry(filepath.Join(bindingRoot, "binding-legacy", "metadata", "username")),
   394  							"password": servicebindings.NewEntry(filepath.Join(bindingRoot, "binding-legacy", "secret", "password")),
   395  						},
   396  					},
   397  				))
   398  			})
   399  
   400  			it("allows 'secret' directory to be omitted", func() {
   401  				err := os.MkdirAll(filepath.Join(bindingRoot, "binding-legacy", "metadata"), os.ModePerm)
   402  				Expect(err).NotTo(HaveOccurred())
   403  
   404  				err = os.WriteFile(filepath.Join(bindingRoot, "binding-legacy", "metadata", "kind"), []byte("type-legacy"), os.ModePerm)
   405  				Expect(err).NotTo(HaveOccurred())
   406  
   407  				err = os.WriteFile(filepath.Join(bindingRoot, "binding-legacy", "metadata", "provider"), []byte("provider-legacy"), os.ModePerm)
   408  				Expect(err).NotTo(HaveOccurred())
   409  
   410  				err = os.WriteFile(filepath.Join(bindingRoot, "binding-legacy", "metadata", "some-key"), nil, os.ModePerm)
   411  				Expect(err).NotTo(HaveOccurred())
   412  
   413  				bindings, err := resolver.Resolve("type-legacy", "", "")
   414  				Expect(err).NotTo(HaveOccurred())
   415  
   416  				Expect(bindings).To(ConsistOf(
   417  					servicebindings.Binding{
   418  						Name:     "binding-legacy",
   419  						Path:     filepath.Join(bindingRoot, "binding-legacy"),
   420  						Type:     "type-legacy",
   421  						Provider: "provider-legacy",
   422  						Entries: map[string]*servicebindings.Entry{
   423  							"some-key": servicebindings.NewEntry(filepath.Join(bindingRoot, "binding-legacy", "metadata", "some-key")),
   424  						},
   425  					},
   426  				))
   427  			})
   428  
   429  			it("returns an error if kind is missing", func() {
   430  				err := os.MkdirAll(filepath.Join(bindingRoot, "bad-binding", "metadata"), os.ModePerm)
   431  				Expect(err).NotTo(HaveOccurred())
   432  
   433  				_, err = resolver.Resolve("bad-type", "", "")
   434  				Expect(err).To(MatchError(HavePrefix("failed to load bindings from '%s': failed to read binding 'bad-binding': missing 'kind'", bindingRoot)))
   435  			})
   436  
   437  			it("returns an error if provider is missing", func() {
   438  				err := os.MkdirAll(filepath.Join(bindingRoot, "bad-binding", "metadata"), os.ModePerm)
   439  				Expect(err).NotTo(HaveOccurred())
   440  
   441  				err = os.WriteFile(filepath.Join(bindingRoot, "bad-binding", "metadata", "kind"), []byte("bad-type"), os.ModePerm)
   442  				Expect(err).NotTo(HaveOccurred())
   443  
   444  				_, err = resolver.Resolve("bad-type", "", "")
   445  				Expect(err).To(MatchError(HavePrefix("failed to load bindings from '%s': failed to read binding 'bad-binding': missing 'provider'", bindingRoot)))
   446  			})
   447  		})
   448  	})
   449  }