mirror of
https://github.com/cunnie/sslip.io.git
synced 2025-10-12 11:00:10 +08:00
Introduce getKv()
, putKv()
, deleteKv()
The key-value portion is about to get a lot more complicated, and as a prelude I've moved the three verbs into their own functions. I plan to do the following: - use an interface for Xip.Etcd (not `*v3client.Client) - use counterfeiter to create fakes for above interface - write vanilla Golang, non-Ginkgo test for getKv, putKv, deleteKv - add local datastructure (non-etcd) to hold kv if no etcd
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
package xip
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
@@ -12,8 +13,6 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
v3client "go.etcd.io/etcd/client/v3"
|
||||
"golang.org/x/net/dns/dnsmessage"
|
||||
)
|
||||
@@ -757,9 +756,7 @@ func (x Xip) kvTXTResources(fqdn string) ([]dnsmessage.TXTResource, error) {
|
||||
labels := strings.Split(fqdn, ".")
|
||||
labels = labels[:len(labels)-3] // strip ".k-v.io"
|
||||
// key is always present, always first subdomain of "k-v.io"
|
||||
// we prepend "d" (data) to differentiate from "t" (time) for future garbage collection
|
||||
keyPrefix := "d"
|
||||
key = keyPrefix + strings.ToLower(labels[len(labels)-1])
|
||||
key = strings.ToLower(labels[len(labels)-1])
|
||||
switch {
|
||||
case len(labels) == 1:
|
||||
verb = "get" // default action if only key, not verb, is not present
|
||||
@@ -771,48 +768,65 @@ func (x Xip) kvTXTResources(fqdn string) ([]dnsmessage.TXTResource, error) {
|
||||
value = strings.Join(labels[1:len(labels)-1], ".") // e.g. "put.94.0.2.firefox-version.k-v.io"
|
||||
}
|
||||
// prepare to query etcd:
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*500)
|
||||
defer cancel()
|
||||
switch verb {
|
||||
case "get":
|
||||
resp, err := x.Etcd.Get(ctx, key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`couldn't GET "%s": %w`, strings.TrimPrefix(key, keyPrefix), err)
|
||||
}
|
||||
if len(resp.Kvs) > 0 {
|
||||
return []dnsmessage.TXTResource{{[]string{string(resp.Kvs[0].Value)}}}, nil
|
||||
}
|
||||
return []dnsmessage.TXTResource{}, nil
|
||||
return x.getKv(key)
|
||||
case "put":
|
||||
if len(labels) == 2 {
|
||||
return []dnsmessage.TXTResource{{[]string{"422: no value provided"}}}, nil
|
||||
return []dnsmessage.TXTResource{{[]string{"422: missing a value: put.value.key.k-v.io"}}}, nil
|
||||
}
|
||||
if len(value) > 63 { // too-long TXT records can be used in DNS amplification attacks; Truncate!
|
||||
value = value[:63]
|
||||
}
|
||||
_, err := x.Etcd.Put(ctx, key, value)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("couldn't PUT (%s: %s): %w", strings.TrimPrefix(key, keyPrefix), value, err)
|
||||
}
|
||||
return []dnsmessage.TXTResource{{[]string{value}}}, nil
|
||||
return x.putKv(key, value)
|
||||
case "delete":
|
||||
getResp, err := x.Etcd.Get(ctx, key) // is the key set?
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`couldn't GET "%s": %w`, strings.TrimPrefix(key, keyPrefix), err)
|
||||
}
|
||||
if len(getResp.Kvs) == 0 { // nothing to delete
|
||||
return []dnsmessage.TXTResource{}, nil
|
||||
}
|
||||
// the key is set; we need to delete it
|
||||
_, err = x.Etcd.Delete(ctx, key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("couldn't DELETE (%s: %s): %w", strings.TrimPrefix(key, keyPrefix), value, err)
|
||||
}
|
||||
return []dnsmessage.TXTResource{{[]string{string(getResp.Kvs[0].Value)}}}, nil
|
||||
return x.deleteKv(key)
|
||||
}
|
||||
return []dnsmessage.TXTResource{{[]string{"422: valid verbs are get, put, delete"}}}, nil
|
||||
}
|
||||
|
||||
func (x Xip) getKv(key string) ([]dnsmessage.TXTResource, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*500)
|
||||
defer cancel()
|
||||
resp, err := x.Etcd.Get(ctx, key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`couldn't GET "%s": %w`, key, err)
|
||||
}
|
||||
if len(resp.Kvs) > 0 {
|
||||
return []dnsmessage.TXTResource{{[]string{string(resp.Kvs[0].Value)}}}, nil
|
||||
}
|
||||
return []dnsmessage.TXTResource{}, nil
|
||||
}
|
||||
|
||||
func (x Xip) putKv(key, value string) ([]dnsmessage.TXTResource, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*500)
|
||||
defer cancel()
|
||||
if len(value) > 63 { // too-long TXT records can be used in DNS amplification attacks; Truncate!
|
||||
value = value[:63]
|
||||
}
|
||||
_, err := x.Etcd.Put(ctx, key, value)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("couldn't PUT (%s: %s): %w", key, value, err)
|
||||
}
|
||||
return []dnsmessage.TXTResource{{[]string{value}}}, nil
|
||||
}
|
||||
|
||||
func (x Xip) deleteKv(key string) ([]dnsmessage.TXTResource, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*500)
|
||||
defer cancel()
|
||||
getResp, err := x.Etcd.Get(ctx, key) // is the key set?
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`couldn't GET "%s": %w`, key, err)
|
||||
}
|
||||
if len(getResp.Kvs) == 0 { // nothing to delete
|
||||
return []dnsmessage.TXTResource{}, nil
|
||||
}
|
||||
value := string(getResp.Kvs[0].Value)
|
||||
// the key is set; we need to delete it
|
||||
_, err = x.Etcd.Delete(ctx, key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("couldn't DELETE (%s: %s): %w", key, value, err)
|
||||
}
|
||||
return []dnsmessage.TXTResource{{[]string{value}}}, nil
|
||||
}
|
||||
|
||||
// soaLogMessage returns an easy-to-read string for logging SOA Answers/Authorities
|
||||
func soaLogMessage(soaResource dnsmessage.SOAResource) string {
|
||||
return soaResource.NS.String() + " " +
|
||||
|
@@ -191,7 +191,7 @@ var _ = Describe("Xip", func() {
|
||||
Entry("getting that deleted value → empty array", "my-key.k-v.io.", []string{}),
|
||||
// errors
|
||||
Entry("getting a non-existent key → empty array", "nonexistent.k-v.io.", []string{}),
|
||||
Entry("putting but skipping the value → error txt", "put.my-key.k-v.io.", []string{"422: no value provided"}),
|
||||
Entry("putting but skipping the value → error txt", "put.my-key.k-v.io.", []string{"422: missing a value: put.value.key.k-v.io"}),
|
||||
Entry("deleting a non-existent key → silently succeeds", "delete.non-existent.k-v.io.", []string{}),
|
||||
Entry("using a garbage verb → error txt", "post.my-key.k-v.io.", []string{"422: valid verbs are get, put, delete"}),
|
||||
// others
|
||||
|
Reference in New Issue
Block a user