| 1 | package provision |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | |
| 6 | "os" |
| 7 | |
| 8 | "github.com/carlosframework/platform/internal/release" |
| 9 | ) |
| 10 | |
| 11 | type RecordIndex struct { |
| 12 | keys map[string][2]string |
| 13 | } |
| 14 | |
| 15 | func IndexRecords(ctx context.Context, s release.Store) (*RecordIndex, error) { |
| 16 | known, partial, err := knownAccounts(ctx, s) |
| 17 | if err != nil { |
| 18 | return nil, err |
| 19 | } |
| 20 | if partial { |
| 21 | return nil, ErrRecordIndexRetry |
| 22 | } |
| 23 | keys, partial, err := listAppSubtree(ctx, s, known, "instances/") |
| 24 | if err != nil { |
| 25 | return nil, err |
| 26 | } |
| 27 | if partial { |
| 28 | return nil, ErrRecordIndexRetry |
| 29 | } |
| 30 | out := &RecordIndex{keys: make(map[string][2]string)} |
| 31 | for _, key := range keys { |
| 32 | a, p, h, ok := ParseRecordKey(key, known) |
| 33 | if !ok { |
| 34 | continue |
| 35 | } |
| 36 | if _, duplicate := out.keys[h]; duplicate { |
| 37 | out.keys[h] = [2]string{} |
| 38 | } else { |
| 39 | out.keys[h] = [2]string{a, p} |
| 40 | } |
| 41 | } |
| 42 | return out, nil |
| 43 | } |
| 44 | |
| 45 | type recordIndexRetry struct{} |
| 46 | |
| 47 | func (recordIndexRetry) Error() string { return os.ErrDeadlineExceeded.Error() } |
| 48 | func (recordIndexRetry) Temporary() bool { return true } |
| 49 | |
| 50 | var ErrRecordIndexRetry error = recordIndexRetry{} |
| 51 | |
| 52 | func (i *RecordIndex) Find(ctx context.Context, s release.Store, host string) (*Record, string, error) { |
| 53 | names, ok := i.keys[host] |
| 54 | if !ok { |
| 55 | return nil, "", os.ErrNotExist |
| 56 | } |
| 57 | account, app := names[0], names[1] |
| 58 | if account == "" { |
| 59 | return nil, "", os.ErrPermission |
| 60 | } |
| 61 | rec, etag, err := GetRecordETag(ctx, s, account, app, host) |
| 62 | if err != nil { |
| 63 | return nil, "", err |
| 64 | } |
| 65 | if etag == "" || rec.Schema != 1 || !release.ValidAccount(account) || !release.ValidApp(app) { |
| 66 | return nil, "", os.ErrPermission |
| 67 | } |
| 68 | if err := ValidateShape(*rec); err != nil { |
| 69 | return nil, "", err |
| 70 | } |
| 71 | return rec, etag, nil |
| 72 | } |
| 73 | |
| 74 | func FindRecordByHost(ctx context.Context, s release.Store, host string) (*Record, string, error) { |
| 75 | index, err := IndexRecords(ctx, s) |
| 76 | if err != nil { |
| 77 | return nil, "", err |
| 78 | } |
| 79 | return index.Find(ctx, s, host) |
| 80 | } |
| 81 | |