Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | cmd/hancock: publish from local to remote store refactor testimony and publish operations, testimony to local (or remote) store, later publish from local to remote |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA3-256: |
88140a8c62832e465df5f75545a43b61 |
User & Date: | dnc 2020-01-03 17:24:53 |
Context
2020-01-03
| ||
17:27 | cmd/hancock: no longer publishing via ipfs check-in: d2f81cb81e user: dnc tags: trunk | |
17:24 | cmd/hancock: publish from local to remote store refactor testimony and publish operations, testimony to local (or remote) store, later publish from local to remote check-in: 88140a8c62 user: dnc tags: trunk | |
17:20 | store: introduce Iterable interface Allow processing of every testimony in local store. check-in: 2009980ac3 user: dnc tags: trunk | |
Changes
Changes to cmd/hancock/publish.go.
1
2
3
4
5
6
7
8
..
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
|
// Copyright (C) 2019 David N. Cohen // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, ................................................................................ // hancock publish // // This operation published the files produced by `hancock testimony`. package main import ( "bytes" "encoding/json" "fmt" "io/ioutil" "net/http" "net/url" "os" "path/filepath" "github.com/pkg/errors" "src.d10.dev/command" "src.d10.dev/hancock/model" ) var ( // Flags that may be used by both publish and testimony operations. storeFlag command.StringSet ) func validateStoreFlag(def string) error { if len(storeFlag) == 0 && def != "" { storeFlag = append(storeFlag, def) } for _, val := range storeFlag { if val == "" { continue } u, err := url.Parse(val) if err != nil { command.Error(errors.Wrapf(err, "bad index URL (%s)", val)) command.Exit() } switch u.Scheme { case "http", "https", "file": // supported scheme default: command.Errorf("bad index (%q): scheme must be http(s):// or file://", val) command.Exit() } } return nil } func init() { command.RegisterOperation(command.Operation{ Handler: publishMain, Name: "publish", Syntax: "publish [-store=<url>] [in=<directory] [testimony ...]", Description: "Publish signed manifest to an index. Expects signed activity on stdin, as encoded by `hancock-sign`.", }) } func publishMain() error { cfg, err := command.Config() command.Check(err) // defaults cfgTop := cfg.Section("") cfgOp := cfg.Section("publish") defaultStore := cfgOp.Key("store").MustString(cfgTop.Key("store").MustString("https://hancock.beyondcentral.com")) defaultDirectory := cfgOp.Key("testimony").MustString(cfgTop.Key("testimony").String()) // args command.OperationFlagSet.Var(&storeFlag, "store", fmt.Sprintf("URL of testimony store (default %q)", defaultStore)) inFlag := command.OperationFlagSet.String("in", defaultDirectory, "directory containing testimony files") // parse flags err = command.OperationFlagSet.Parse(command.Args()[1:]) if err != nil { return err } // validate flags err = validateStoreFlag(defaultStore) if err != nil { return err } if len(storeFlag) == 0 { return errors.New("Storage URL required. Use flag `-store <URL>` or see configuration file.") } if len(command.OperationFlagSet.Args()) == 0 && *inFlag == "" { return errors.New("Testimony required. Use `-in <directory>` or specify testimony files.") } if *inFlag != "" { inStat, err := os.Stat(*inFlag) if os.IsNotExist(err) { return fmt.Errorf("Input directory does not exist (%q).", *inFlag) } if !inStat.IsDir() { return fmt.Errorf("Not a directory (%q).", *inFlag) } } in := command.OperationFlagSet.Args() if len(in) == 0 { in = append(in, *inFlag) } // TODO(dnc): concurrency for _, fileOrDir := range in { count := 0 err = filepath.Walk(fileOrDir, func(filepath string, info os.FileInfo, err error) error { if err != nil { return err } if info.IsDir() { return nil // noop, continue walk (we only process files, not directories) } f, err := os.Open(filepath) if err != nil { return err } defer f.Close() dec := json.NewDecoder(f) var testimony fileTestimony err = dec.Decode(&testimony) if err != nil { return err } err = testimony.FileManifest.Check() if err != nil { return err } // validate testimony here? stored := false for _, storeURL := range storeFlag { err = publishTestimonyToStore(storeURL, testimony) if err != nil { command.Error(err) } else { stored = true } } if stored { count++ } return err }) command.V(1).Infof("Published %d items from %q.", count, fileOrDir) if err != nil { command.Error(err) } } return nil } func publishTestimonyToIPFS(ipfsURL string, ft fileTestimony) error { err := ipfsAdd(ipfsURL, ft.Bytes(), ft.FileName()) return err } // publish testimony either to local filesystem or via POST to online index. func publishTestimonyToStore(storeURL string, ft fileTestimony) error { u, err := url.Parse(storeURL) if err != nil { return err } err = ft.Check() if err != nil { |
|
<
>
>
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
>
<
<
<
>
<
>
|
<
<
|
>
|
<
>
>
|
|
|
|
>
>
<
<
>
>
>
>
>
>
|
<
|
<
<
<
<
<
<
|
>
|
|
<
<
>
|
>
>
<
>
>
<
>
|
<
<
<
<
<
<
|
|
<
<
<
<
<
<
<
<
<
<
<
|
<
|
|
<
<
<
|
|
>
|
1
2
3
4
5
6
7
8
..
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
|
// Copyright (C) 2019, 2020 David N. Cohen // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, ................................................................................ // hancock publish // // This operation published the files produced by `hancock testimony`. package main import ( "bytes" "fmt" "io/ioutil" "log" "net/http" "net/url" "os" "path/filepath" "github.com/pkg/errors" "src.d10.dev/command" "src.d10.dev/hancock/model" "src.d10.dev/hancock/store" ) func init() { command.RegisterOperation(command.Operation{ Handler: publishMain, Name: "publish", Syntax: "publish [-store=<destination>] <source> [...]", Description: "Publish signed testimony from a local store to a remote store. Use `hancock testimony` to populate local store.", }) } func publishMain() error { cfg, err := command.Config() command.Check(err) // defaults cfgTop := cfg.Section("") cfgOp := cfg.Section("publish") defaultStore := cfgOp.Key("store").MustString(cfgTop.Key("store").MustString("https://hancock.beyondcentral.com")) // args storeFlag := command.OperationFlagSet.String("store", "defaultStore", fmt.Sprintf("URL of remote testimony store (default %q)", defaultStore)) // parse flags err = command.ParseOperationFlagSet() if err != nil { return err } // validate flags remote, err := storeFromFlag(*storeFlag) if err != nil { return err } defer closeStore() // closes all results of storeFromFlag() if len(command.OperationFlagSet.Args()) == 0 { return errors.New("Local testimony store required.") } // first loop, validate that local store supports Iterable for _, arg := range command.OperationFlagSet.Args() { in, err := storeFromFlag(arg) if err != nil { command.Error(fmt.Errorf("failed to open local testimony store (%q): %w", arg, err)) return nil } // input stores must implement Iterable (output does not need to) _, ok := in.(store.Iterable) if !ok { command.Errorf("Cannot publish from testimony store (%q): not iterable", arg) return nil } } pubs := 0 for _, arg := range command.OperationFlagSet.Args() { tmp, err := storeFromFlag(arg) if err != nil { command.Error(fmt.Errorf("failed to open local testimony store (%q): %w", arg, err)) return nil } in := tmp.(store.Iterable) err = in.ForEach(func(key model.TestimonyKey, testimony model.Testimony) error { // TODO(dnc): concurrency, for performance // sanity check (should not be needed) err := testimony.Check() if err != nil { return err } // publish testimony from local to remote k, err := remote.PutTestimony(testimony) if err != nil { return err } command.V(1).Infof("published %v to %v", k, remote) pubs++ return nil }) if err != nil { command.Error(err) } } command.Infof("published %d items to %q", pubs, remote) return nil } // publish testimony either to local filesystem or via POST to online index. func XXXpublishTestimonyToStore(storeURL string, ft fileTestimony) error { log.Panic("replace calls to publishTestimonyToStore() with store.Store interface!") u, err := url.Parse(storeURL) if err != nil { return err } err = ft.Check() if err != nil { |
Changes to cmd/hancock/testimony.go.
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 .. 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 ... 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
import ( "encoding/json" "fmt" "io" "log" "os" "github.com/pkg/errors" "golang.org/x/crypto/ssh" "src.d10.dev/command" "src.d10.dev/hancock/model" ) func init() { ................................................................................ // defaults cfgTop := cfg.Section("") cfgTestimony := cfg.Section("testimony") defaultStore := cfgTestimony.Key("store").MustString(cfgTop.Key("store").String()) // define args command.OperationFlagSet.Var(&storeFlag, "store", fmt.Sprintf("URL where testimony is saved/published (default %q)", defaultStore)) noopFlag := command.OperationFlagSet.Bool("n", false, "do not save/publish") // parse args err = command.OperationFlagSet.Parse(command.Args()[1:]) if err != nil { return err } // validate args err = validateStoreFlag(defaultStore) if err != nil { return err } if len(storeFlag) == 0 { return errors.New("Storage URL required. Use flag `-store <URL>` or see configuration file.") } defer closeStore() // cleanup; safe to call even if no stores initialized // decode input dec := json.NewDecoder(os.Stdin) // first input, the public key (corresponding to signing private key) var authorityEncoded model.Authority ................................................................................ Authority: authorityEncoded, Content: man.Content, Encoded: rawMan, Signature: sig, } if !*noopFlag { //ft := fileTestimony{Testimony: testimony, FileManifest: man} // testimony with decoded manifest stored := false for _, storeURL := range storeFlag { if storeURL == "" { continue // explicit -index="" on command line } stor, err := storeFromFlag(storeURL) if err != nil { command.Errorf("failed to publish testimony (%q) to %q: %s", man.Path, storeURL, err) continue } tkey, err := stor.PutTestimony(testimony) if err != nil { command.Errorf("failed to publish testimony (%q) to %q: %s", man.Path, storeURL, err) continue } stored = true command.V(2).Infof("published testimony (%q) to %q; key: %v", man.Path, storeURL, tkey) } if stored { command.V(1).Infof("%q testimony published", man.Path) } } } return nil } |
< | | < < < < < < < < < < < < < < | | | | | < < < < | < |
26 27 28 29 30 31 32 33 34 35 36 37 38 39 .. 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 ... 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
import ( "encoding/json" "fmt" "io" "log" "os" "golang.org/x/crypto/ssh" "src.d10.dev/command" "src.d10.dev/hancock/model" ) func init() { ................................................................................ // defaults cfgTop := cfg.Section("") cfgTestimony := cfg.Section("testimony") defaultStore := cfgTestimony.Key("store").MustString(cfgTop.Key("store").String()) // define args storeFlag := command.OperationFlagSet.String("store", defaultStore, "URL where testimony is saved/published") noopFlag := command.OperationFlagSet.Bool("n", false, "do not save/publish") // parse args err = command.OperationFlagSet.Parse(command.Args()[1:]) if err != nil { return err } // validate args remote, err := storeFromFlag(*storeFlag) if err != nil { return err } defer closeStore() // cleanup; safe to call even if no stores initialized // decode input dec := json.NewDecoder(os.Stdin) // first input, the public key (corresponding to signing private key) var authorityEncoded model.Authority ................................................................................ Authority: authorityEncoded, Content: man.Content, Encoded: rawMan, Signature: sig, } if !*noopFlag { tkey, err := remote.PutTestimony(testimony) if err != nil { command.Errorf("failed to publish testimony (%q) to %q: %s", man.Path, remote, err) continue } command.V(1).Infof("%q testimony key: %s", man.Path, tkey) } } return nil } |