package engine import ( "os" "path/filepath" "strings" "testing" "jarl/internal/model" ) func TestZoneDisable(t *testing.T) { env := newEnv(t) setupZone(env) z := env.state().ZonesNamed("example.com")[0] confPath := filepath.Join(env.dir, "etc/named.conf") zonePath := filepath.Join(env.dir, "zones/_default/example.com.zone") res := env.submit(env.admin, NewRequest(OpZoneUpdate, z.ID, ZoneInput{Disabled: model.Ptr(true)})) if res.Ops[0].Summary != "disable zone example.com" || len(res.Warnings) != 1 || !strings.Contains(res.Warnings[0], "recursion") { t.Errorf("disable: %q, warnings %v", res.Ops[0].Summary, res.Warnings) } conf, _ := os.ReadFile(confPath) if strings.Contains(string(conf), "zone \"example.com\" {") || !strings.Contains(string(conf), `// zone "example.com" is disabled in Jarl`) { t.Errorf("named.conf with the zone disabled:\n%s", conf) } // Still edited, and its file kept up to date. env.submit(env.admin, rec("example.com", "www", "A", "192.0.2.10")) if f, err := os.ReadFile(zonePath); err != nil || !strings.Contains(string(f), "www\t\tIN\tA\t192.0.2.10") { t.Errorf("zone file while disabled: %v\n%s", err, f) } // Enabling brings it back. res = env.submit(env.admin, NewRequest(OpZoneUpdate, z.ID, ZoneInput{Disabled: model.Ptr(false)})) if res.Ops[0].Summary != "enable zone example.com" { t.Errorf("enable: %q", res.Ops[0].Summary) } if conf, _ := os.ReadFile(confPath); !strings.Contains(string(conf), "zone \"example.com\" {") { t.Errorf("named.conf after enabling:\n%s", conf) } // Reverting a disable enables the zone again. res = env.submit(env.admin, NewRequest(OpZoneUpdate, z.ID, ZoneInput{Disabled: model.Ptr(true)})) rev, err := env.e.Revert(env.ctx, res.ChangesetID, env.admin) if err != nil { t.Fatal(err) } if _, err := env.e.Apply(env.ctx, rev.ID, env.admin, ApplyOptions{}); err != nil || env.state().Zones[z.ID].Disabled { t.Errorf("revert of a disable: %v, disabled=%v", err, env.state().Zones[z.ID].Disabled) } // Signed zones can't be disabled. env.submit(env.admin, NewRequest(OpZoneUpdate, z.ID, ZoneInput{DNSSECPolicy: model.Ptr("default")})) if e := env.submitErr(env.admin, NewRequest(OpZoneUpdate, z.ID, ZoneInput{Disabled: model.Ptr(true)})); e.Kind != KindValidation || !strings.Contains(e.Message, "signed zone can't be disabled") { t.Errorf("disabling a signed zone: %+v", e) } } func TestZoneDisableInView(t *testing.T) { env := newEnv(t) setupZone(env) env.submit(env.admin, NewRequest(OpViewCreate, 0, ViewInput{Name: model.Ptr("internal"), MatchClients: &[]string{"10.0.0.0/8"}, AdoptZones: true}), NewRequest(OpViewCreate, 0, ViewInput{Name: model.Ptr("external")}), ) st := env.state() ext := st.ViewByName("external") env.submit(env.admin, NewRequest(OpZoneCreate, 0, ZoneInput{Name: model.Ptr("example.com"), ViewID: &ext.ID, Type: model.Ptr(model.ZoneInView), InView: model.Ptr("internal")})) in := st.ZonesNamed("example.com")[0] // The zone another view shares can't be disabled alone. if e := env.submitErr(env.admin, NewRequest(OpZoneUpdate, in.ID, ZoneInput{Disabled: model.Ptr(true)})); e.Kind != KindValidation || !strings.Contains(e.Message, "is disabled, so this zone can't share it") { t.Errorf("disabling a shared zone: %+v", e) } // Without recursion, the warning says REFUSED. env.submit(env.admin, NewRequest(OpOptionCreate, 0, OptionInput{Scope: model.Ptr(model.ScopeView), ScopeID: &ext.ID, Name: model.Ptr("recursion"), Value: model.Ptr("no")})) env.submit(env.admin, NewRequest(OpZoneCreate, 0, ZoneInput{Name: model.Ptr("public.example"), ViewID: &ext.ID})) pub := env.state().FindZone("public.example", ext.ID) res := env.submit(env.admin, NewRequest(OpZoneUpdate, pub.ID, ZoneInput{Disabled: model.Ptr(true)})) if len(res.Warnings) != 1 || !strings.Contains(res.Warnings[0], "view external answers REFUSED") { t.Errorf("warnings = %v", res.Warnings) } }