Migrate Notarius to credential lists
This commit is contained in:
@@ -309,7 +309,7 @@ type ScriptoriumInputConfig struct {
|
||||
type NotariusConfig struct {
|
||||
Enabled bool `yaml:"enabled" json:"enabled"`
|
||||
Binary string `yaml:"binary" json:"binary"`
|
||||
LLMAPIKey *configkit.Secret `yaml:"llm_api_key,omitempty" json:"llm_api_key,omitempty"`
|
||||
Credentials []configkit.Secret `yaml:"credentials,omitempty" json:"credentials,omitempty"`
|
||||
ConfigProfile *string `yaml:"config_profile,omitempty" json:"config_profile,omitempty"`
|
||||
Resume bool `yaml:"resume,omitempty" json:"resume,omitempty"`
|
||||
ChunkCache string `yaml:"chunk_cache,omitempty" json:"chunk_cache,omitempty"`
|
||||
|
||||
@@ -50,12 +50,12 @@ func (cfg AuditaConfig) MarshalYAML() (any, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// MarshalYAML exposes only the public Notarius credential reference name.
|
||||
// MarshalYAML exposes only public Notarius credential reference names.
|
||||
func (cfg NotariusConfig) MarshalYAML() (any, error) {
|
||||
type safeNotarius struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
Binary string `yaml:"binary"`
|
||||
LLMAPIKey *string `yaml:"llm_api_key,omitempty"`
|
||||
Credentials []string `yaml:"credentials,omitempty"`
|
||||
ConfigProfile *string `yaml:"config_profile,omitempty"`
|
||||
Resume bool `yaml:"resume,omitempty"`
|
||||
ChunkCache string `yaml:"chunk_cache,omitempty"`
|
||||
@@ -67,7 +67,7 @@ func (cfg NotariusConfig) MarshalYAML() (any, error) {
|
||||
Outputs map[string]NotariusOutputConfig `yaml:"outputs"`
|
||||
}
|
||||
return safeNotarius{
|
||||
Enabled: cfg.Enabled, Binary: cfg.Binary, LLMAPIKey: secretReferenceName(cfg.LLMAPIKey),
|
||||
Enabled: cfg.Enabled, Binary: cfg.Binary, Credentials: secretReferenceNames(cfg.Credentials),
|
||||
ConfigProfile: cfg.ConfigProfile,
|
||||
Resume: cfg.Resume, ChunkCache: cfg.ChunkCache,
|
||||
ConfigPath: cfg.ConfigPath, PipelineID: cfg.PipelineID, Timeout: cfg.Timeout,
|
||||
@@ -75,6 +75,17 @@ func (cfg NotariusConfig) MarshalYAML() (any, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func secretReferenceNames(secrets []configkit.Secret) []string {
|
||||
if secrets == nil {
|
||||
return nil
|
||||
}
|
||||
names := make([]string, len(secrets))
|
||||
for index, secret := range secrets {
|
||||
names[index] = secret.EnvName()
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
func secretReferenceName(secret *configkit.Secret) *string {
|
||||
if secret == nil {
|
||||
return nil
|
||||
|
||||
@@ -102,7 +102,7 @@ storage:
|
||||
secret_access_key: NARRATIO_TEST_SECRET_KEY
|
||||
notarius:
|
||||
enabled: false
|
||||
llm_api_key: NARRATIO_TEST_NOTARIUS_KEY
|
||||
credentials: [NARRATIO_TEST_NOTARIUS_KEY]
|
||||
config_profile: ""
|
||||
`
|
||||
pipelinePath, _ := writeConfigFiles(t, pipelineYAML, testSessionBaseYAML)
|
||||
@@ -146,8 +146,8 @@ notarius:
|
||||
}
|
||||
var roundTrip struct {
|
||||
Notarius *struct {
|
||||
LLMAPIKey *string `yaml:"llm_api_key"`
|
||||
ConfigProfile *string `yaml:"config_profile"`
|
||||
Credentials []string `yaml:"credentials"`
|
||||
ConfigProfile *string `yaml:"config_profile"`
|
||||
} `yaml:"notarius"`
|
||||
}
|
||||
if err := yaml.Unmarshal(firstYAML, &roundTrip); err != nil {
|
||||
@@ -156,8 +156,8 @@ notarius:
|
||||
if roundTrip.Notarius == nil || roundTrip.Notarius.ConfigProfile == nil || *roundTrip.Notarius.ConfigProfile != "" {
|
||||
t.Fatalf("round-trip config profile = %#v, want explicit empty", roundTrip.Notarius)
|
||||
}
|
||||
if roundTrip.Notarius.LLMAPIKey == nil || *roundTrip.Notarius.LLMAPIKey != "NARRATIO_TEST_NOTARIUS_KEY" {
|
||||
t.Fatalf("round-trip credential reference = %#v, want safe public name", roundTrip.Notarius.LLMAPIKey)
|
||||
if len(roundTrip.Notarius.Credentials) != 1 || roundTrip.Notarius.Credentials[0] != "NARRATIO_TEST_NOTARIUS_KEY" {
|
||||
t.Fatalf("round-trip credential reference = %#v, want safe public name", roundTrip.Notarius.Credentials)
|
||||
}
|
||||
|
||||
t.Setenv("NARRATIO_TEST_ACCESS_KEY", secondCanary)
|
||||
|
||||
@@ -58,8 +58,10 @@ notarius:
|
||||
|
||||
func TestNotariusCredentialReferenceLoading(t *testing.T) {
|
||||
const envName = "NARRATIO_TEST_NOTARIUS_KEY"
|
||||
const secondName = "NARRATIO_TEST_NOTARIUS_SECOND_KEY"
|
||||
const canary = "notarius-credential-canary"
|
||||
t.Setenv(envName, canary)
|
||||
t.Setenv(secondName, "second-canary")
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -67,8 +69,9 @@ func TestNotariusCredentialReferenceLoading(t *testing.T) {
|
||||
wantNil bool
|
||||
}{
|
||||
{name: "omitted", wantNil: true},
|
||||
{name: "null", credential: " llm_api_key: null\n", wantNil: true},
|
||||
{name: "custom reference", credential: " llm_api_key: " + envName + "\n"},
|
||||
{name: "null", credential: " credentials: null\n", wantNil: true},
|
||||
{name: "empty", credential: " credentials: []\n"},
|
||||
{name: "custom references", credential: " credentials: [" + envName + ", " + secondName + "]\n"},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
@@ -81,15 +84,19 @@ func TestNotariusCredentialReferenceLoading(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if test.wantNil {
|
||||
if cfg.Notarius.LLMAPIKey != nil {
|
||||
t.Fatal("credential reference should be nil")
|
||||
}
|
||||
if test.wantNil && cfg.Notarius.Credentials != nil {
|
||||
t.Fatal("credential list should be nil")
|
||||
}
|
||||
if test.name == "empty" && len(cfg.Notarius.Credentials) == 0 {
|
||||
return
|
||||
}
|
||||
if cfg.Notarius.LLMAPIKey == nil || cfg.Notarius.LLMAPIKey.EnvName() != envName ||
|
||||
!cfg.Notarius.LLMAPIKey.IsResolved() || cfg.Notarius.LLMAPIKey.Value() != canary {
|
||||
t.Fatal("credential reference was not loaded and resolved")
|
||||
if test.wantNil {
|
||||
return
|
||||
}
|
||||
if len(cfg.Notarius.Credentials) != 2 ||
|
||||
cfg.Notarius.Credentials[0].EnvName() != envName || cfg.Notarius.Credentials[0].Value() != canary ||
|
||||
cfg.Notarius.Credentials[1].EnvName() != secondName || cfg.Notarius.Credentials[1].Value() != "second-canary" {
|
||||
t.Fatal("credential references were not loaded and resolved")
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -110,7 +117,7 @@ func TestNotariusCredentialReferenceFailures(t *testing.T) {
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
path := filepath.Join(t.TempDir(), "pipeline.yml")
|
||||
pipelineYAML := testPipelineBaseYAML + "\nnotarius:\n enabled: false\n llm_api_key: " + test.reference + "\n"
|
||||
pipelineYAML := testPipelineBaseYAML + "\nnotarius:\n enabled: false\n credentials: [" + test.reference + "]\n"
|
||||
if err := os.WriteFile(path, []byte(pipelineYAML), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -122,6 +129,17 @@ func TestNotariusCredentialReferenceFailures(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestNotariusRejectsRetiredCredentialField(t *testing.T) {
|
||||
path := filepath.Join(t.TempDir(), "pipeline.yml")
|
||||
pipelineYAML := testPipelineBaseYAML + "\nnotarius:\n llm_api_key: NARRATIO_TEST_NOTARIUS_KEY\n"
|
||||
if err := os.WriteFile(path, []byte(pipelineYAML), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := LoadPipeline(path); err == nil || !strings.Contains(err.Error(), "decode.unknown_field") {
|
||||
t.Fatalf("LoadPipeline() error = %v, want unknown field", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNotariusEnabledDefaultsAndPathResolution(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
pipelinePath := filepath.Join(dir, "deployment", "pipeline.yml")
|
||||
|
||||
@@ -147,7 +147,7 @@ func TestPipelineOverlayDoesNotRequireUnusedBaseCredentials(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestNotariusCredentialProfileReplacementAndProvenance(t *testing.T) {
|
||||
func TestNotariusCredentialOverlayReplacementAndProvenance(t *testing.T) {
|
||||
const baseKey = "NARRATIO_NOTARIUS_UNUSED_BASE_KEY"
|
||||
const profileKey = "NARRATIO_NOTARIUS_PROFILE_KEY"
|
||||
if err := os.Unsetenv(baseKey); err != nil {
|
||||
@@ -155,24 +155,24 @@ func TestNotariusCredentialProfileReplacementAndProvenance(t *testing.T) {
|
||||
}
|
||||
t.Setenv(profileKey, "synthetic-notarius-profile-key")
|
||||
|
||||
for _, credential := range []string{profileKey, "null"} {
|
||||
for _, credential := range []string{"[" + profileKey + "]", "null", "[]"} {
|
||||
t.Run(credential, func(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
root := writePipelineFixture(t, dir, "pipeline.yml", "whisperx:\n transcribe_url: https://transcription.example.test/transcribe\nnotarius:\n enabled: false\n llm_api_key: "+baseKey+"\n")
|
||||
overlay := writePipelineFixture(t, dir, "overlays/testing.yml", "notarius:\n llm_api_key: "+credential+"\n")
|
||||
root := writePipelineFixture(t, dir, "pipeline.yml", "whisperx:\n transcribe_url: https://transcription.example.test/transcribe\nnotarius:\n enabled: false\n credentials: ["+baseKey+"]\n")
|
||||
overlay := writePipelineFixture(t, dir, "overlays/testing.yml", "notarius:\n credentials: "+credential+"\n")
|
||||
profile := "testing"
|
||||
cfg, err := LoadPipelineWithOptions(root, PipelineLoadOptions{Overlay: &profile})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if credential == "null" {
|
||||
if cfg.Notarius.LLMAPIKey != nil {
|
||||
t.Fatal("profile did not remove the base credential")
|
||||
if credential == "null" || credential == "[]" {
|
||||
if len(cfg.Notarius.Credentials) != 0 {
|
||||
t.Fatal("overlay did not clear base credentials")
|
||||
}
|
||||
return
|
||||
}
|
||||
if cfg.Notarius.LLMAPIKey == nil || cfg.Notarius.LLMAPIKey.EnvName() != profileKey || !cfg.Notarius.LLMAPIKey.IsResolved() {
|
||||
t.Fatal("profile credential was not resolved")
|
||||
if len(cfg.Notarius.Credentials) != 1 || cfg.Notarius.Credentials[0].EnvName() != profileKey || !cfg.Notarius.Credentials[0].IsResolved() {
|
||||
t.Fatal("overlay credential was not resolved")
|
||||
}
|
||||
sources, err := EffectivePipelineSources(cfg, ResolvedParty{})
|
||||
if err != nil {
|
||||
@@ -180,12 +180,12 @@ func TestNotariusCredentialProfileReplacementAndProvenance(t *testing.T) {
|
||||
}
|
||||
found := false
|
||||
for _, source := range sources {
|
||||
if source.Path == "notarius.llm_api_key" {
|
||||
if source.Path == "notarius.credentials" {
|
||||
found = source.Source == overlay
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Fatal("Notarius credential provenance did not identify the selected profile")
|
||||
t.Fatal("Notarius credential provenance did not identify the selected overlay")
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -234,7 +234,7 @@ func TestNotariusCredentialRootProvenance(t *testing.T) {
|
||||
const envName = "NARRATIO_NOTARIUS_ROOT_KEY"
|
||||
t.Setenv(envName, "synthetic-notarius-root-key")
|
||||
dir := t.TempDir()
|
||||
root := writePipelineFixture(t, dir, "pipeline.yml", "whisperx:\n transcribe_url: https://transcription.example.test/transcribe\nnotarius:\n enabled: false\n llm_api_key: "+envName+"\n")
|
||||
root := writePipelineFixture(t, dir, "pipeline.yml", "whisperx:\n transcribe_url: https://transcription.example.test/transcribe\nnotarius:\n enabled: false\n credentials: ["+envName+"]\n")
|
||||
cfg, err := LoadPipeline(root)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -244,7 +244,7 @@ func TestNotariusCredentialRootProvenance(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, source := range sources {
|
||||
if source.Path == "notarius.llm_api_key" && source.Source == root {
|
||||
if source.Path == "notarius.credentials" && source.Source == root {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user