package main import ( "os" "path/filepath" "strings" "testing" ) // Where a builder publishes. // // Preferably from the mesh: a builder that is a module requires an artifact store, and the mesh // writes it a binding saying which machine answers and on what port. Reading it means the address // is not a setting somebody keeps in step by hand. func binding(t *testing.T, body string) string { t.Helper() path := filepath.Join(t.TempDir(), "artifact-store.json") if err := os.WriteFile(path, []byte(body), 0o644); err != nil { t.Fatal(err) } return path } func TestTheMeshSaysWhereToPublish(t *testing.T) { t.Setenv("MESH_BINDING", binding(t, `{"binding":1,"provision":"artifact-store", "from":"anchor","at":"anchor.internal","serves":{"port":5000,"scheme":"http"}}`)) where, err := whereToPublish() if err != nil { t.Fatal(err) } if where != "anchor.internal:5000" { t.Fatalf("got %q", where) } } func TestABindingWithNoAddressIsRefused(t *testing.T) { // The provider is not on the private network, so there is no name to reach it by. Falling // back to anything would publish to a store on the wrong machine and be found out much later. t.Setenv("MESH_BINDING", binding(t, `{"binding":1,"from":"anchor","serves":{"port":5000}}`)) _, err := whereToPublish() if err == nil { t.Fatal("a binding with nowhere to reach was accepted") } if !strings.Contains(err.Error(), "anchor") { t.Fatalf("the failure does not name the machine: %v", err) } } func TestABindingWithNoPortIsRefused(t *testing.T) { t.Setenv("MESH_BINDING", binding(t, `{"binding":1,"from":"a","at":"a.internal","serves":{}}`)) if _, err := whereToPublish(); err == nil { t.Fatal("a binding saying nothing about a port was accepted") } } func TestTheVariableStillWorksForABuilderRunByAPerson(t *testing.T) { // Which is how this started and how it is still run while being developed. t.Setenv("MESH_BINDING", "") t.Setenv("MESH_REGISTRY", "127.0.0.1:5000") where, err := whereToPublish() if err != nil { t.Fatal(err) } if where != "127.0.0.1:5000" { t.Fatalf("got %q", where) } } func TestNeitherIsRefusedRatherThanGuessed(t *testing.T) { t.Setenv("MESH_BINDING", "") t.Setenv("MESH_REGISTRY", "") if _, err := whereToPublish(); err == nil { t.Fatal("a builder with nowhere to publish reported somewhere") } } func TestTheCredentialComesFromAFileTheMeshSealed(t *testing.T) { // A builder that is a module is given its credential the way every module is: sealed to the // machine and written by the host. An environment variable instead would put the one copy // that matters through a terminal and a process listing. path := filepath.Join(t.TempDir(), "broker") if err := os.WriteFile(path, []byte("amqps://a-builder:secret@broker.internal:5671/\n"), 0o600); err != nil { t.Fatal(err) } t.Setenv("MESH_BROKER_FILE", path) t.Setenv("MESH_BROKER_AMQP", "amqp://should-not-be-used@nowhere/") got, err := brokerFrom() if err != nil { t.Fatal(err) } if got.URL != "amqps://a-builder:secret@broker.internal:5671/" { t.Fatalf("got %q", got.URL) } } // The credential the mesh seals carries what to check the broker's certificate against, because a // mesh's broker presents a certificate of the mesh's own and no public trust store has it. A URL // alone can only reach a broker somebody else vouches for. func TestTheSealedCredentialCarriesWhatVerifiesTheBroker(t *testing.T) { path := filepath.Join(t.TempDir(), "broker") if err := os.WriteFile(path, []byte( `{"url":"amqps://a-builder:secret@broker.internal:5671/","fingerprint":"abc123"}`), 0o600); err != nil { t.Fatal(err) } t.Setenv("MESH_BROKER_FILE", path) t.Setenv("MESH_BROKER_AMQP", "") got, err := brokerFrom() if err != nil { t.Fatal(err) } if got.URL != "amqps://a-builder:secret@broker.internal:5671/" { t.Fatalf("the url was lost: %q", got.URL) } if got.Fingerprint != "abc123" { t.Fatal("the builder was given nothing to check the broker against, so it can only " + "connect to a broker some public authority vouches for") } } func TestAnEmptyCredentialFileIsRefused(t *testing.T) { // Otherwise the builder connects as nobody and is refused, with the reason three layers away. path := filepath.Join(t.TempDir(), "broker") if err := os.WriteFile(path, []byte("\n"), 0o600); err != nil { t.Fatal(err) } t.Setenv("MESH_BROKER_FILE", path) t.Setenv("MESH_BROKER_AMQP", "") if _, err := brokerFrom(); err == nil { t.Fatal("an empty credential was accepted") } } func TestABuilderWithNoCredentialAtAllSaysSo(t *testing.T) { t.Setenv("MESH_BROKER_FILE", "") t.Setenv("MESH_BROKER_AMQP", "") if _, err := brokerFrom(); err == nil { t.Fatal("a builder with no broker reported one") } }