func (s *HTTPServer) KVSEndpoint(resp http.ResponseWriter, req *http.Request) (interface{}, error) { // Set default DC args := structs.KeyRequest{} if done := s.parse(resp, req, &args.Datacenter, &args.QueryOptions); done { return nil, nil } // Pull out the key name, validation left to each sub-handler args.Key = strings.TrimPrefix(req.URL.Path, "/v1/kv/") // Check for a key list keyList := false params := req.URL.Query() if _, ok := params["keys"]; ok { keyList = true } // Switch on the method switch req.Method { case "GET": if keyList { return s.KVSGetKeys(resp, req, &args) } else { return s.KVSGet(resp, req, &args) } case "PUT": return s.KVSPut(resp, req, &args) case "DELETE": return s.KVSDelete(resp, req, &args) default: resp.WriteHeader(405) return nil, nil } }
// remoteExecGetSpec is used to get the exec specification. // Returns if execution should continue func (a *Agent) remoteExecGetSpec(event *remoteExecEvent, spec *remoteExecSpec) bool { get := structs.KeyRequest{ Datacenter: a.config.Datacenter, Key: path.Join(event.Prefix, event.Session, remoteExecFileName), QueryOptions: structs.QueryOptions{ AllowStale: true, // Stale read for scale! Retry on failure. }, } get.Token = a.config.ACLToken var out structs.IndexedDirEntries QUERY: if err := a.RPC("KVS.Get", &get, &out); err != nil { a.logger.Printf("[ERR] agent: failed to get remote exec job: %v", err) return false } if len(out.Entries) == 0 { // If the initial read was stale and had no data, retry as a consistent read if get.QueryOptions.AllowStale { a.logger.Printf("[DEBUG] agent: trying consistent fetch of remote exec job spec") get.QueryOptions.AllowStale = false goto QUERY } else { a.logger.Printf("[DEBUG] agent: remote exec aborted, job spec missing") return false } } if err := json.Unmarshal(out.Entries[0].Value, &spec); err != nil { a.logger.Printf("[ERR] agent: failed to decode remote exec spec: %v", err) return false } return true }
func getKV(t *testing.T, agent *Agent, key string) *structs.DirEntry { req := structs.KeyRequest{ Datacenter: agent.config.Datacenter, Key: key, } req.Token = agent.config.ACLToken var out structs.IndexedDirEntries if err := agent.RPC("KVS.Get", &req, &out); err != nil { t.Fatalf("err: %v", err) } if len(out.Entries) > 0 { return out.Entries[0] } return nil }
func TestKVSEndpoint_List_Blocking(t *testing.T) { dir1, s1 := testServer(t) defer os.RemoveAll(dir1) defer s1.Shutdown() client := rpcClient(t, s1) defer client.Close() testutil.WaitForLeader(t, client.Call, "dc1") keys := []string{ "/test/key1", "/test/key2", "/test/sub/key3", } for _, key := range keys { arg := structs.KVSRequest{ Datacenter: "dc1", Op: structs.KVSSet, DirEnt: structs.DirEntry{ Key: key, Flags: 1, }, } var out bool if err := client.Call("KVS.Apply", &arg, &out); err != nil { t.Fatalf("err: %v", err) } } getR := structs.KeyRequest{ Datacenter: "dc1", Key: "/test", } var dirent structs.IndexedDirEntries if err := client.Call("KVS.List", &getR, &dirent); err != nil { t.Fatalf("err: %v", err) } // Setup a blocking query getR.MinQueryIndex = dirent.Index getR.MaxQueryTime = time.Second // Async cause a change start := time.Now() go func() { time.Sleep(100 * time.Millisecond) client := rpcClient(t, s1) defer client.Close() arg := structs.KVSRequest{ Datacenter: "dc1", Op: structs.KVSDelete, DirEnt: structs.DirEntry{ Key: "/test/sub/key3", }, } var out bool if err := client.Call("KVS.Apply", &arg, &out); err != nil { t.Fatalf("err: %v", err) } }() // Re-run the query dirent = structs.IndexedDirEntries{} if err := client.Call("KVS.List", &getR, &dirent); err != nil { t.Fatalf("err: %v", err) } // Should block at least 100ms if time.Now().Sub(start) < 100*time.Millisecond { t.Fatalf("too fast") } if dirent.Index == 0 { t.Fatalf("Bad: %v", dirent) } if len(dirent.Entries) != 2 { for _, ent := range dirent.Entries { t.Errorf("Bad: %#v", *ent) } } for i := 0; i < len(dirent.Entries); i++ { d := dirent.Entries[i] if d.Key != keys[i] { t.Fatalf("bad: %v", d) } if d.Flags != 1 { t.Fatalf("bad: %v", d) } if d.Value != nil { t.Fatalf("bad: %v", d) } } }