Merge ~knitzsche/snappy-hwe-snaps/+git/wifi-connect:defaults into ~snappy-hwe-team/snappy-hwe-snaps/+git/wifi-connect:master
- Git
- lp:~knitzsche/snappy-hwe-snaps/+git/wifi-connect
- defaults
- Merge into master
Status: | Merged |
---|---|
Approved by: | Roberto Mier Escandon |
Approved revision: | 174a30c8d126a5c65dd79770c04e9561a52b0c16 |
Merged at revision: | d8fe23b5e46dd61a32c597d146aa434feb2958cd |
Proposed branch: | ~knitzsche/snappy-hwe-snaps/+git/wifi-connect:defaults |
Merge into: | ~snappy-hwe-team/snappy-hwe-snaps/+git/wifi-connect:master |
Diff against target: |
941 lines (+700/-38) 15 files modified
README.md (+1/-1) daemon/daemon.go (+66/-6) daemon/daemon_test.go (+121/-20) docs/faq.md (+10/-0) docs/index.md (+24/-0) docs/installation.md (+121/-0) docs/integrate.md (+70/-0) docs/metadata.yaml (+12/-0) hooks/configure.go (+111/-0) hooks/configure_test.go (+110/-0) service/service.go (+14/-6) snap/snapcraft.yaml (+8/-3) static/tests/pre-config0.json (+7/-0) static/tests/pre-config1.json (+5/-0) wifiap/wifiap.go (+20/-2) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Roberto Mier Escandon (community) | Approve | ||
Konrad Zapałowicz (community) | code | Approve | |
System Enablement Bot | continuous-integration | Approve | |
Review via email: mp+326172@code.launchpad.net |
Commit message
Description of the change
Add configure hook to port certain snap wifi-connect keys into SNAP_COMMON/
Deamon (service/service.go and daemon pkg) uses SetDefaults() to use the json to take the appropriate actions. [1]
docs/ added this, including integrate.md to doc the exactl preconfiguration stuff for system intergrators.
wifiap/wifiap.go: added Wifiaper interface to make wifiap unit-testable in SetDefaults()
Enhanced daemon_test.go TestSetDefaults to handle various preconfigs via static/
System Enablement Bot (system-enablement-ci-bot) wrote : | # |
System Enablement Bot (system-enablement-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:bbe03fe8158
https:/
Executed test runs:
SUCCESS: https:/
None: https:/
SUCCESS: https:/
None: https:/
Click here to trigger a rebuild:
https:/
Sheila Miguez (codersquid) wrote : | # |
minor inline comment
System Enablement Bot (system-enablement-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:dbbc07aaae5
https:/
Executed test runs:
SUCCESS: https:/
None: https:/
SUCCESS: https:/
None: https:/
Click here to trigger a rebuild:
https:/
Sheila Miguez (codersquid) wrote : | # |
Ignore my inline comment, I missed where the user gets feedback way below.
Konrad Zapałowicz (kzapalowicz) wrote : | # |
Comments inline.
Roberto Mier Escandon (rmescandon) wrote : | # |
I've left a pair of inline comments.
Kyle Nitzsche (knitzsche) wrote : | # |
After discussion with Konrad, I'll leave the markdown as is since I consistently use code blocks.
After discussion with Roberto, I'll leave the name of "Wifiaper" interface as is for consistency with golang interface naming conventions.
Kyle Nitzsche (knitzsche) wrote : | # |
replied to all other comments inline, mostly saying "yes you are right", one way or another ;)
System Enablement Bot (system-enablement-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:1406f57787f
https:/
Executed test runs:
SUCCESS: https:/
None: https:/
SUCCESS: https:/
None: https:/
Click here to trigger a rebuild:
https:/
System Enablement Bot (system-enablement-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:925edb1d205
https:/
Executed test runs:
SUCCESS: https:/
None: https:/
SUCCESS: https:/
None: https:/
Click here to trigger a rebuild:
https:/
System Enablement Bot (system-enablement-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:37dddc026a3
https:/
Executed test runs:
SUCCESS: https:/
None: https:/
SUCCESS: https:/
None: https:/
Click here to trigger a rebuild:
https:/
System Enablement Bot (system-enablement-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:aa641828629
https:/
Executed test runs:
SUCCESS: https:/
None: https:/
SUCCESS: https:/
None: https:/
Click here to trigger a rebuild:
https:/
Roberto Mier Escandon (rmescandon) wrote : | # |
> After discussion with Konrad, I'll leave the markdown as is since I
> consistently use code blocks.
>
> After discussion with Roberto, I'll leave the name of "Wifiaper" interface as
> is for consistency with golang interface naming conventions.
A note here about the name of the interface:
https:/
Kyle Nitzsche (knitzsche) wrote : | # |
thanks for pointing this out Roberto
On 06/26/2017 07:09 AM, Roberto Mier Escandón wrote:
>> After discussion with Konrad, I'll leave the markdown as is since I
>> consistently use code blocks.
>>
>> After discussion with Roberto, I'll leave the name of "Wifiaper" interface as
>> is for consistency with golang interface naming conventions.
>
> A note here about the name of the interface:
>
> https:/
>
Kyle Nitzsche (knitzsche) wrote : | # |
READY for review - thanks
System Enablement Bot (system-enablement-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:f89788ca7ad
https:/
Executed test runs:
SUCCESS: https:/
None: https:/
SUCCESS: https:/
None: https:/
Click here to trigger a rebuild:
https:/
Roberto Mier Escandon (rmescandon) wrote : | # |
Some inline comments
Kyle Nitzsche (knitzsche) wrote : | # |
Roberto, pls see my responses, a couple small code changes coming.
Roberto Mier Escandon (rmescandon) wrote : | # |
rereplied
Kyle Nitzsche (knitzsche) wrote : | # |
On 06/27/2017 09:08 AM, Roberto Mier Escandón wrote:
> rereplied
>
> Diff comments:
>
>> diff --git a/daemon/
>> index c6242cd..a2bfca3 100644
>> --- a/daemon/
>> +++ b/daemon/
>> @@ -94,8 +98,76 @@ func TestManualMode(t *testing.T) {
>> }
>> }
>>
>> +type mockWifiap struct{}
>> +
>> +//var wifiapClient wifiapInterface
>
> commented on purpose?
>
>> +
>> +func (mock *mockWifiap) Do(req *http.Request) (*http.Response, error) {
>> + fmt.Println("==== MY do called")
>> + url := req.URL.String()
>> + if url != "http://
>> + return nil, fmt.Errorf("Not valid request URL: %v", url)
>> + }
>> +
>> + if req.Method != "GET" {
>> + return nil, fmt.Errorf("Method is not valid. Expected GET, got %v", req.Method)
>> + }
>> +
>> + rawBody := `{"result":{
>> + "debug":false,
>> + "dhcp.lease-time": "12h",
>> + "dhcp.range-start": "10.0.60.2",
>> + "dhcp.range-stop": "10.0.60.199",
>> + "disabled": true,
>> + "share.disabled": false,
>> + "share-
>> + "wifi-address": "10.0.60.1",
>> + "wifi.channel": "6",
>> + "wifi.hostapd-
>> + "wifi.interface": "wlan0",
>> + "wifi.interface
>> + "wifi.netmask": "255.255.255.0",
>> + "wifi.operation
>> + "wifi.security": "
>> + "wifi.security-
>> + "wifi.ssid": "AP"},"
>> +
>> + response := http.Response{
>> + StatusCode: 200,
>> + Status: "200 OK",
>> + Body: ioutil.
>> + }
>> +
>> + return &response, nil
>> +}
>> +
>> +func (mock *mockWifiap) Show() (map[string]
>> + wifiAp := make(map[
>> + wifiAp[
>> + return wifiAp, nil
>> +}
>> +
>> +func (mock *mockWifiap) Enabled() (bool, error) {
>> + return true, nil
>> +}
>> +
>> +func (mock *mockWifiap) Enable() error {
>> + return nil
>> +}
>> +func (mock *mockWifiap) Disable() error {
>> + return nil
>> +}
>> +func (mock *mockWifiap) SetSsid(s string) error {
>> + return nil
>> +}
>> +
>> +func (mock *mockWifiap) SetPassphrase(p string) error {
>> + return nil
>> +}
>> +
>> func TestSetDefaults(t *testing.T) {
>> client := GetClient()
>> + PreConfigFile = "../static/
>> hfp := "/tmp/hash"
>> if _, err := os.Stat(hfp); err == nil {
>> err = os.Remove(hfp)
>> diff --git a/hooks/
>> new file mode 100644
>> index 0000000..c674cca
>> --- /dev/null
>> +++ b/hooks/
>> @@ -0,0 +1,111 @@
>> +// -*- Mode: Go; indent-tabs-mode: t -*-
>> +
>> +/*
>> + * Copyright (C) 2017 Canonical Ltd
>> + *
>> + * This program is free software: you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 3 as
>> + * published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>> + ...
Roberto Mier Escandon (rmescandon) wrote : | # |
1) can we agree to disagree on this or do you feel strongly?
+1. This is not crucial.
2) That's not quite the reason I tried to explain previously. Should we discuss in hang out so we don't keep going round in circles on this point?
Np. This way also is valid and are the names I'm using in config-ui pr. +1 also
Konrad Zapałowicz (kzapalowicz) wrote : | # |
Some more comments, this SetDefaults thing is an interesting exercise! I'm half-way through, will finish later today.
Konrad Zapałowicz (kzapalowicz) wrote : | # |
Finished
System Enablement Bot (system-enablement-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:bc0d115ab1e
https:/
Executed test runs:
SUCCESS: https:/
None: https:/
SUCCESS: https:/
None: https:/
Click here to trigger a rebuild:
https:/
System Enablement Bot (system-enablement-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:67d24fcb7b0
https:/
Executed test runs:
SUCCESS: https:/
None: https:/
SUCCESS: https:/
None: https:/
Click here to trigger a rebuild:
https:/
Kyle Nitzsche (knitzsche) wrote : | # |
Ready for re-review - pls see comments above (although lp formatting seems to have injected numerous extra "*"s ;)
System Enablement Bot (system-enablement-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:4b79f17e60f
https:/
Executed test runs:
SUCCESS: https:/
None: https:/
SUCCESS: https:/
None: https:/
Click here to trigger a rebuild:
https:/
System Enablement Bot (system-enablement-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:174a30c8d12
https:/
Executed test runs:
SUCCESS: https:/
None: https:/
SUCCESS: https:/
None: https:/
Click here to trigger a rebuild:
https:/
Konrad Zapałowicz (kzapalowicz) wrote : | # |
ack
Roberto Mier Escandon (rmescandon) : | # |
Preview Diff
1 | diff --git a/README.md b/README.md |
2 | index d100e2f..1759797 100644 |
3 | --- a/README.md |
4 | +++ b/README.md |
5 | @@ -44,7 +44,7 @@ snap connect wifi-connect:network-manager network-manager:service |
6 | Note: wifi-ap and network-manager interfaces auto-connect. |
7 | Note: Currently content share interface requires a reboot after connection, as described below. |
8 | |
9 | -# Set NetWorkManager to control all networking |
10 | +# Set NetworkManager to control all networking |
11 | |
12 | Note: This is a temporary manual step before network-manager snap provides a config option for this. |
13 | |
14 | diff --git a/daemon/daemon.go b/daemon/daemon.go |
15 | index 04ff59e..a888969 100644 |
16 | --- a/daemon/daemon.go |
17 | +++ b/daemon/daemon.go |
18 | @@ -18,8 +18,11 @@ |
19 | package daemon |
20 | |
21 | import ( |
22 | + "encoding/json" |
23 | "fmt" |
24 | + "io/ioutil" |
25 | "os" |
26 | + "path/filepath" |
27 | |
28 | "launchpad.net/wifi-connect/avahi" |
29 | "launchpad.net/wifi-connect/server" |
30 | @@ -40,6 +43,19 @@ var waitFlagPath string |
31 | var previousState = STARTING |
32 | var state = STARTING |
33 | |
34 | +// PreConfigFile is the path to the file that stores the hash of the portals password |
35 | +var PreConfigFile = filepath.Join(os.Getenv("SNAP_COMMON"), "pre-config.json") |
36 | + |
37 | +// PreConfig is the struct representing a configuration |
38 | +type PreConfig struct { |
39 | + Passphrase string `json:"wifi.security-passphrase,omitempty"` |
40 | + Ssid string `json:"wifi.ssid,omitempty"` |
41 | + Interface string `json:"wifi.interface,omitempty"` |
42 | + Password string `json:"portal.password,omitempty"` |
43 | + NoOperational bool `json:"portal.no-operational,omitempty"` //whether to show the operational portal |
44 | + NoResetCreds bool `json:"portal.no-reset-creds,omitempty"` //whether user must reset passphrase and password on first use of mgmt portal |
45 | +} |
46 | + |
47 | // Client is the base type for both testing and runtime |
48 | type Client struct { |
49 | } |
50 | @@ -189,11 +205,55 @@ func (c *Client) OperationalServerDown() { |
51 | } |
52 | } |
53 | |
54 | -// SetDefaults sets defaults if not yet set. Currently the hash |
55 | -// for the portals password is set. |
56 | -// TODO: set default password based on MAC addr or Serial number |
57 | -func (c *Client) SetDefaults() { |
58 | - if _, err := os.Stat(utils.HashFile); os.IsNotExist(err) { |
59 | - utils.HashIt("wifi-connect") |
60 | +// LoadPreConfig returns a PreConfig based on the pre-config.json, if present, and an error to indicate |
61 | +// possible json unmarshal failure |
62 | +func LoadPreConfig() (*PreConfig, error) { |
63 | + config := &PreConfig{} |
64 | + content, err := ioutil.ReadFile(PreConfigFile) |
65 | + if err == nil { |
66 | + fmt.Println("== wifi-connect/daemon: preconfiguration file found") |
67 | + } |
68 | + err = json.Unmarshal(content, config) |
69 | + if err != nil { |
70 | + return config, err |
71 | + } |
72 | + return config, nil |
73 | +} |
74 | + |
75 | +// SetDefaults creates the run time configuration based on wifi-ap and the pre-config.json |
76 | +// configuration file, if any. The configuration is returned with an error. PreConfig.PreConfigfile |
77 | +// indicates whether a pre-config file exists. |
78 | +func (c *Client) SetDefaults(cw wifiap.Operations, config *PreConfig) error { |
79 | + if err != nil { |
80 | + fmt.Println("== wifi-connect/daemon/SetDefaults: preconfig unmarshall errorr:", err) |
81 | + } |
82 | + ap, errShow := cw.Show() |
83 | + if errShow != nil { |
84 | + fmt.Println("== wifi-connect/daemon/SetDefaults: wifi-ap.Show err:", errShow) |
85 | + } |
86 | + if ap["wifi.security-passphrase"] != config.Passphrase { |
87 | + if len(config.Passphrase) > 0 { |
88 | + err = cw.SetPassphrase(config.Passphrase) |
89 | + fmt.Println("== wifi-connect/SetDefaults wifi-ap passphrase being set") |
90 | + if err != nil { |
91 | + fmt.Println("== wifi-connect/daemon/SetDefaults: passphrase err:", err) |
92 | + return err |
93 | + } |
94 | + } |
95 | + } |
96 | + if len(config.Password) > 0 { |
97 | + fmt.Println("== wifi-connect/SetDefaults portal password being set") |
98 | + _, err = utils.HashIt(config.Password) |
99 | + if err != nil { |
100 | + fmt.Println("== wifi-connect/daemon/SetDefaults: password err:", err) |
101 | + return err |
102 | + } |
103 | + } |
104 | + if config.NoOperational { |
105 | + fmt.Println("== wifi-connect/SetDefaults: operational portal is now disabled") |
106 | + } |
107 | + if config.NoResetCreds { |
108 | + fmt.Println("== wifi-connect/SetDefaults: reset creds requirement is now disabled") |
109 | } |
110 | + return nil |
111 | } |
112 | diff --git a/daemon/daemon_test.go b/daemon/daemon_test.go |
113 | index c6242cd..177c26b 100644 |
114 | --- a/daemon/daemon_test.go |
115 | +++ b/daemon/daemon_test.go |
116 | @@ -18,7 +18,11 @@ |
117 | package daemon |
118 | |
119 | import ( |
120 | + "fmt" |
121 | + "io/ioutil" |
122 | + "net/http" |
123 | "os" |
124 | + "strings" |
125 | "testing" |
126 | |
127 | "launchpad.net/wifi-connect/utils" |
128 | @@ -94,8 +98,92 @@ func TestManualMode(t *testing.T) { |
129 | } |
130 | } |
131 | |
132 | +type mockWifiap struct{} |
133 | + |
134 | +func (mock *mockWifiap) Do(req *http.Request) (*http.Response, error) { |
135 | + fmt.Println("==== MY do called") |
136 | + url := req.URL.String() |
137 | + if url != "http://unix/v1/configuration" { |
138 | + return nil, fmt.Errorf("Not valid request URL: %v", url) |
139 | + } |
140 | + |
141 | + if req.Method != "GET" { |
142 | + return nil, fmt.Errorf("Method is not valid. Expected GET, got %v", req.Method) |
143 | + } |
144 | + |
145 | + rawBody := `{"result":{ |
146 | + "debug":false, |
147 | + "dhcp.lease-time": "12h", |
148 | + "dhcp.range-start": "10.0.60.2", |
149 | + "dhcp.range-stop": "10.0.60.199", |
150 | + "disabled": true, |
151 | + "share.disabled": false, |
152 | + "share-network-interface": "tun0", |
153 | + "wifi-address": "10.0.60.1", |
154 | + "wifi.channel": "6", |
155 | + "wifi.hostapd-driver": "nl80211", |
156 | + "wifi.interface": "wlan0", |
157 | + "wifi.interface-mode": "direct", |
158 | + "wifi.netmask": "255.255.255.0", |
159 | + "wifi.operation-mode": "g", |
160 | + "wifi.security": " |
161 | + "wifi.security-passphrase": "passphrase123", |
162 | + "wifi.ssid": "AP"},"status":"OK","status-code":200,""sync"}` |
163 | + |
164 | + response := http.Response{ |
165 | + StatusCode: 200, |
166 | + Status: "200 OK", |
167 | + Body: ioutil.NopCloser(strings.NewReader(rawBody)), |
168 | + } |
169 | + |
170 | + return &response, nil |
171 | +} |
172 | + |
173 | +func (mock *mockWifiap) Show() (map[string]interface{}, error) { |
174 | + wifiAp := make(map[string]interface{}) |
175 | + wifiAp["wifi.security-passphrase"] = "randompassphrase" |
176 | + return wifiAp, nil |
177 | +} |
178 | + |
179 | +func (mock *mockWifiap) Enabled() (bool, error) { |
180 | + return true, nil |
181 | +} |
182 | + |
183 | +func (mock *mockWifiap) Enable() error { |
184 | + return nil |
185 | +} |
186 | +func (mock *mockWifiap) Disable() error { |
187 | + return nil |
188 | +} |
189 | +func (mock *mockWifiap) SetSsid(s string) error { |
190 | + return nil |
191 | +} |
192 | + |
193 | +func (mock *mockWifiap) SetPassphrase(p string) error { |
194 | + return nil |
195 | +} |
196 | + |
197 | +func TestLoadPreConfig(t *testing.T) { |
198 | + PreConfigFile = "../static/tests/pre-config0.json" |
199 | + config, err := LoadPreConfig() |
200 | + if err != nil { |
201 | + t.Errorf("Unexpected error using LoadPreConfig: %s", err) |
202 | + } |
203 | + if config.Passphrase != "abcdefghijklmnop" { |
204 | + t.Errorf("Passphrase of %s expected but got %s:", "abcdefghijklmnop", config.Passphrase) |
205 | + } |
206 | + if !config.NoOperational { |
207 | + t.Errorf("portal.no-operational was set to true but the loaded config is %t", config.NoOperational) |
208 | + } |
209 | + if !config.NoResetCreds { |
210 | + t.Errorf("portal.no-reset-creds was set to true but the loaded config is %t", config.NoResetCreds) |
211 | + } |
212 | + |
213 | +} |
214 | + |
215 | func TestSetDefaults(t *testing.T) { |
216 | client := GetClient() |
217 | + PreConfigFile = "../static/tests/pre-config0.json" |
218 | hfp := "/tmp/hash" |
219 | if _, err := os.Stat(hfp); err == nil { |
220 | err = os.Remove(hfp) |
221 | @@ -103,35 +191,48 @@ func TestSetDefaults(t *testing.T) { |
222 | t.Errorf("Could not remove previous file version") |
223 | } |
224 | } |
225 | + config, _ := LoadPreConfig() |
226 | utils.SetHashFile(hfp) |
227 | - client.SetDefaults() |
228 | - _, err := os.Stat(utils.HashFile) |
229 | + client.SetDefaults(&mockWifiap{}, config) |
230 | + expectedPassphrase := "abcdefghijklmnop" |
231 | + expectedPassword := "qwerzxcv" |
232 | + if config.Passphrase != expectedPassphrase { |
233 | + t.Errorf("SetDefaults: Preconfig passphrase should be %s but is %s", expectedPassphrase, config.Passphrase) |
234 | + } |
235 | if os.IsNotExist(err) { |
236 | t.Errorf("SetDefaults should have created %s but did not", hfp) |
237 | } |
238 | - res, _ := utils.MatchingHash("wifi-connect") |
239 | + res, _ := utils.MatchingHash(expectedPassword) |
240 | if !res { |
241 | - t.Errorf("SetDefaults password match did not match") |
242 | + t.Errorf("SetDefaults: Preconfig password hash did not match actual") |
243 | + } |
244 | + if !config.NoOperational { |
245 | + t.Errorf("SetDefaults: Preconfig portal.no-operational should be true (set) but is %t", config.NoOperational) |
246 | + } |
247 | + if !config.NoResetCreds { |
248 | + t.Errorf("SetDefaults: Preconfig portal.no-reset-creds should be true (set) but is %t", config.NoResetCreds) |
249 | } |
250 | -} |
251 | |
252 | -func TestSetDefaultsAlreadyExistsHashFile(t *testing.T) { |
253 | - client := GetClient() |
254 | - hfp := "/tmp/hash" |
255 | - // create file if not exists |
256 | - if _, err := os.Stat(utils.HashFile); os.IsNotExist(err) { |
257 | - if _, err = os.OpenFile(hfp, os.O_CREATE, 0666); err != nil { |
258 | - t.Errorf("Error creating %v file", hfp) |
259 | + if _, err := os.Stat(hfp); err == nil { |
260 | + err = os.Remove(hfp) |
261 | + if err != nil { |
262 | + t.Errorf("Could not remove previous file version") |
263 | } |
264 | } |
265 | - utils.SetHashFile(hfp) |
266 | - client.SetDefaults() |
267 | - _, err := os.Stat(utils.HashFile) |
268 | - if os.IsNotExist(err) { |
269 | - t.Errorf("SetDefaults should have created %s but did not", hfp) |
270 | + PreConfigFile = "../static/tests/pre-config1.json" |
271 | + config, _ = LoadPreConfig() |
272 | + client.SetDefaults(&mockWifiap{}, config) |
273 | + if len(config.Passphrase) > 0 { |
274 | + t.Errorf("SetDefaults: Preconfig passphrase was not set but is %s", config.Passphrase) |
275 | } |
276 | - res, _ := utils.MatchingHash("wifi-connect") |
277 | - if !res { |
278 | - t.Errorf("SetDefaults password match did not match") |
279 | + res2, _ := utils.MatchingHash(expectedPassword) |
280 | + if res2 { |
281 | + t.Errorf("SetDefaults: Preconfig password was not set, but the hash matched") |
282 | + } |
283 | + if config.NoOperational { |
284 | + t.Errorf("SetDefaults: Preconfig portal.no-operational should be false (unset) but is %t", config.NoOperational) |
285 | + } |
286 | + if config.NoResetCreds { |
287 | + t.Errorf("SetDefaults: Preconfig portal.no-reset-creds should be false (unnset) but is %t", config.NoResetCreds) |
288 | } |
289 | } |
290 | diff --git a/docs/faq.md b/docs/faq.md |
291 | new file mode 100644 |
292 | index 0000000..ec8f87e |
293 | --- /dev/null |
294 | +++ b/docs/faq.md |
295 | @@ -0,0 +1,10 @@ |
296 | +--- |
297 | +title: "Frequently asked questions" |
298 | +table_of_contents: True |
299 | +--- |
300 | + |
301 | +# Restarting the wifi-connect daemon |
302 | + |
303 | +```bash |
304 | +sudo systemctl restart snap.wifi-connect.daemon.service |
305 | +``` |
306 | diff --git a/docs/index.md b/docs/index.md |
307 | new file mode 100644 |
308 | index 0000000..679b180 |
309 | --- /dev/null |
310 | +++ b/docs/index.md |
311 | @@ -0,0 +1,24 @@ |
312 | +--- |
313 | +title: "Overview" |
314 | +table_of_contents: True |
315 | +--- |
316 | + |
317 | +# Overview |
318 | + |
319 | +The wifi-connect snap allows you to connect your device to an external Wi-Fi access point. It does this by putting up its own Wi-Fi AP and web page. You join the AP and the web page lists external APs, which you can then select and join. |
320 | + |
321 | +Wifi-connect is appropriate for simple use cases where there is no other control of networking. Wifi-connect has a daemon that takes over networking and controls device state automatically: |
322 | + |
323 | + * When there is no external AP connection, wifi-connect starts its own AP and the Management web page (which allows you to select and join external WiFI APs) |
324 | + * When there is an external AP connection, wifi-connect ensures its own AP is down and puts up the Operational web page (which allows you to disconnect from the external AP) |
325 | + |
326 | +Wifi-connect uses two other snaps: |
327 | + |
328 | + * wifi-ap: provides the AP function |
329 | + * network-manager: handles networking (as a part of installation, the device netplan is modified to make network-manager the renderer for all networking). |
330 | + |
331 | +Wifi-connect can be: |
332 | + |
333 | + * Installed at run time |
334 | + * Integrated into an image, with options. See "Integrating into an Image" section |
335 | + |
336 | diff --git a/docs/installation.md b/docs/installation.md |
337 | new file mode 100644 |
338 | index 0000000..deba1ec |
339 | --- /dev/null |
340 | +++ b/docs/installation.md |
341 | @@ -0,0 +1,121 @@ |
342 | +--- |
343 | +title: "Installation" |
344 | +table_of_contents: True |
345 | +--- |
346 | + |
347 | +# Overview |
348 | + |
349 | +The wifi-connect snap is currently publish in edge and beta channels. |
350 | + |
351 | +## Install snaps |
352 | + |
353 | +```bash |
354 | +snap install wifi-ap |
355 | +snap install network-manager |
356 | +snap install --edge|beta wifi-connect |
357 | +``` |
358 | + |
359 | +## Connect interfaces |
360 | + |
361 | +```bash |
362 | +snap connect wifi-connect:control wifi-ap:control |
363 | +snap connect wifi-connect:network core:network |
364 | +snap connect wifi-connect:network-bind core:network-bind |
365 | +snap connect wifi-connect:network-manager network-manager:service |
366 | +``` |
367 | + |
368 | +## Set NetWorkManager to control all networking |
369 | + |
370 | +**Note**: This is a temporary manual step before network-manager snap provides a config option for this. |
371 | + |
372 | +**Note**: Depending on your environment, after this you may need to use a new IP address to connect to the device. |
373 | + |
374 | + 1. Backup the existing /etc/netplan/00-snapd-config.yaml file |
375 | + |
376 | + sudo mv /etc/netplan/00-snapd-config.yaml ~/ |
377 | + |
378 | + 1. Create a new netplan config file named /etc/netplan/00-default-nm-renderer.yaml: |
379 | + |
380 | + sudo vi /etc/netplan/00-default-nm-renderer.yaml |
381 | + |
382 | + Add the following two lines: |
383 | + |
384 | + network: |
385 | + renderer: NetworkManager |
386 | + |
387 | +## Reboot |
388 | + |
389 | +Rebooting addresses a potential content sharing interface issue. |
390 | + |
391 | +Rebooting also consolidates all networking into NetworkManager. |
392 | + |
393 | +## Optionally configure wifi-ap SSID/passphrase |
394 | + |
395 | +If you skip these steps, the wifi-AP put up by the device has an SSID of "Ubuntu" and is unsecure (with no passphrase). |
396 | + |
397 | + 1. Set the wifi-ap AP SSID |
398 | + |
399 | + sudo wifi-connect ssid MYSSID |
400 | + |
401 | + 1. Set the AP passphrase: |
402 | + |
403 | + sudo wifi-connect passphrase MYPASSPHRASE |
404 | + |
405 | +## Display the AP config |
406 | + |
407 | +```bash |
408 | +sudo wifi-connect show-ap |
409 | +``` |
410 | + |
411 | +**Note** the DHCP range: |
412 | + |
413 | + dhcp.range-start: 10.0.60.2 |
414 | + dhcp.range-stop: 10.0.60.199 |
415 | + |
416 | +## Set the portal password |
417 | + |
418 | +The portal password must be entered to access wifi-connect web pages. |
419 | + |
420 | +```bash |
421 | +sudo wifi-connect set-portal-password PASSWORD |
422 | +``` |
423 | + |
424 | +## Join the device AP |
425 | + |
426 | +When the device AP is up and available to you, join it. |
427 | + |
428 | +## Open the the Management portal web page |
429 | + |
430 | +This portal displays external wifi APs and let's you join them. |
431 | + |
432 | +After you connect to the device AP, you can open its http portal at the .1 IP address just before the start of the DHCP range (see previous steps) using port 8080: |
433 | + |
434 | + 10.0.60.1:8080 |
435 | + |
436 | +You then need to enter the portal password to continue. |
437 | + |
438 | +### Avahi and hostname |
439 | + |
440 | +You can also connect to the device's web page using the device host name: |
441 | + |
442 | + http://HOSTNAME.local:8080 |
443 | + |
444 | +Where HOSTNAME is the hostname of the device when it booted. (Changing hostname with the hostname command at run time is not sufficient.) |
445 | + |
446 | +**Note**: The system trying to open the web page must support Avahi. Android systems may not, for example. |
447 | + |
448 | +## Be patient, it takes minutes |
449 | + |
450 | +Wifi-connect pauses for about a minute at daemon start to allow any external AP connections to complete. |
451 | + |
452 | +## Disconnect from wifi |
453 | + |
454 | +When connected to an external AP, the Operational portal is available on the device IP address (assigned by the external AP). Open it using IP:8080, enter the portal password, and you may then disconnect with the "Disconnect from Wifi" button. |
455 | + |
456 | +You can also ssh to the device and: |
457 | + |
458 | + * Use `nmcli c` to display connections. |
459 | + * Use `nmcli c delete CONNECTION_NAME` to disconnect and delete. This puts the device into management mode, bringing up the AP and portal. |
460 | + |
461 | +Disconnecting sets the device back in Management mode. Its AP is started and you can open the portal (as discussed above) to see external APs and connect to one. |
462 | + |
463 | diff --git a/docs/integrate.md b/docs/integrate.md |
464 | new file mode 100644 |
465 | index 0000000..b1cdd4f |
466 | --- /dev/null |
467 | +++ b/docs/integrate.md |
468 | @@ -0,0 +1,70 @@ |
469 | +--- |
470 | +title: "Integrating into an image"" |
471 | +table_of_contents: True |
472 | +--- |
473 | + |
474 | +# Overview |
475 | + |
476 | +When you pre-install wifi-connect snap into an image, you can use the gadget snap's [gadget.yaml](https://forum.snapcraft.io/t/the-gadget-snap/696) file to pre-configure some options. |
477 | + |
478 | +Here we explain the snap key and value needed to preconfigure. Refer to the above like for details on how to set these in the gadget snap's gadget.yaml file. |
479 | + |
480 | +You may also set these at run time from terminal with: |
481 | + |
482 | +```bash |
483 | +$ snap set wifi-connect KEY=VALUE |
484 | +``` |
485 | + |
486 | +To apply such run-time changes, see the Frequently asked questions page. |
487 | + |
488 | +**Warning**: These changes may create security risks. Only use take these steps if you are completely aware of take responsitbility for the potential risk. |
489 | + |
490 | + |
491 | +**Note** When the deamon starts, it logs any preconfigurations found and applied, for example: |
492 | + |
493 | +```bash |
494 | +Jun 20 22:07:16 thehost snap[18004]: == wifi-connect/SetDefaults portal password being set |
495 | +Jun 20 22:07:16 thehost snap[18004]: == wifi-connect/SetDefaults: reset creds requirement is now disabled |
496 | +``` |
497 | + |
498 | +## AP passphrase |
499 | + |
500 | +Normally the wifi-conect AP's passphrase is randomly created by the wifi-ap snap. A normal part of the installation process is resetting this from the terminal. However, some integrators may want to preset the passphrase. |
501 | + |
502 | +Preset the passphrase with the following: |
503 | + |
504 | + * snapd key: wifi.security-passphrase |
505 | + * value: the passhrase (8-13 characters, must start with a letter) |
506 | + |
507 | +## Portal password |
508 | + |
509 | +To access any wifi-connect web page you need to enter the portal password. A normal part of the installation process is setting this from the terminal. However, some integrators may want to preset the portal password. |
510 | + |
511 | +Preset the portal password with the following: |
512 | + |
513 | + * snapd key: portal.password |
514 | + * value: the password (8-13 characters, must start with a letter) |
515 | + |
516 | +## Disable credential resetting |
517 | + |
518 | +Normally, the first user to access the Management portal is required to reset the wifi-connect AP passphrase and the portal password (used to access wifi-connect web pages). This is an important security feature, especially for integrators that preset these in their image because every image has the same passphrase and password. |
519 | + |
520 | +However, some integrators may want to disable this requirement. |
521 | + |
522 | +Disable the requirement to reset the AP passphrase and portal password on first use of the Management portal as follows: |
523 | + |
524 | + * snapd key: portal.no-reset-creds |
525 | + * value: true |
526 | + |
527 | +## Disable display of the Operational Portal |
528 | + |
529 | +The Operational portal is available when the device is connected to an external AP. It provides a button the user can click to disconnect. Display of this page can be disabled. This is a normal part of wifi-connect. When the page is disabled one must use the terminal (or some other means) to direct the device to disconnect form the external AP. |
530 | + |
531 | +Disable the Operational portal as follows: |
532 | + |
533 | + * snapd key: portal.no-opertaional |
534 | + * value: true |
535 | + |
536 | + |
537 | + |
538 | + |
539 | diff --git a/docs/metadata.yaml b/docs/metadata.yaml |
540 | new file mode 100644 |
541 | index 0000000..2fb6ea0 |
542 | --- /dev/null |
543 | +++ b/docs/metadata.yaml |
544 | @@ -0,0 +1,12 @@ |
545 | +site_title: wifi-connect |
546 | +site_logo_url: https://assets.ubuntu.com/v1/c5cb0f8e-picto-ubuntu.svg |
547 | +navigation: |
548 | + - title: Introduction |
549 | + location: index.md |
550 | + children: |
551 | + - title: Installation |
552 | + location: installation.md |
553 | + - title: Integrating into an image |
554 | + location: integrate.md |
555 | + - title: Frequently asked questions |
556 | + location: faq.md |
557 | diff --git a/hooks/configure.go b/hooks/configure.go |
558 | new file mode 100644 |
559 | index 0000000..601a93a |
560 | --- /dev/null |
561 | +++ b/hooks/configure.go |
562 | @@ -0,0 +1,111 @@ |
563 | +// -*- Mode: Go; indent-tabs-mode: t -*- |
564 | + |
565 | +/* |
566 | + * Copyright (C) 2017 Canonical Ltd |
567 | + * |
568 | + * This program is free software: you can redistribute it and/or modify |
569 | + * it under the terms of the GNU General Public License version 3 as |
570 | + * published by the Free Software Foundation. |
571 | + * |
572 | + * This program is distributed in the hope that it will be useful, |
573 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
574 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
575 | + * GNU General Public License for more details. |
576 | + * |
577 | + * You should have received a copy of the GNU General Public License |
578 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
579 | + * |
580 | + */ |
581 | + |
582 | +package main |
583 | + |
584 | +import ( |
585 | + "encoding/json" |
586 | + "io/ioutil" |
587 | + "log" |
588 | + "os/exec" |
589 | + "strings" |
590 | + |
591 | + "launchpad.net/wifi-connect/daemon" |
592 | +) |
593 | + |
594 | +// Client is the base struct for runtime and testing |
595 | +type Client struct { |
596 | + getter Getter |
597 | +} |
598 | + |
599 | +// Get is the test obj for overridding functions |
600 | +type Get struct{} |
601 | + |
602 | +// Getter interface is for overriding SnapGet for testing |
603 | +type Getter interface { |
604 | + SnapGet(string) (string, error) |
605 | +} |
606 | + |
607 | +// GetClient returns a normal runtime client |
608 | +func GetClient() *Client { |
609 | + return &Client{getter: &Get{}} |
610 | +} |
611 | + |
612 | +// ModClient returns a testing client |
613 | +func ModClient(g Getter) *Client { |
614 | + return &Client{getter: g} |
615 | +} |
616 | + |
617 | +// SnapGet uses snapctrl to get a value from a key, or returns error |
618 | +func (g *Get) SnapGet(key string) (string, error) { |
619 | + out, err := exec.Command("snapctl", "get", key).Output() |
620 | + if err != nil { |
621 | + return "", err |
622 | + } |
623 | + return strings.TrimSpace(string(out)), nil |
624 | + |
625 | +} |
626 | + |
627 | +// snapGetStr wraps SnapGet for string types and verifies the snap var is valid |
628 | +func (c *Client) snapGetStr(key string, target *string) { |
629 | + val, err := c.getter.SnapGet(key) |
630 | + if err != nil { |
631 | + return |
632 | + } |
633 | + if len(val) == 0 { |
634 | + log.Printf("== wifi-connect/configure error: key %s exists but has zero length", key) |
635 | + return |
636 | + } |
637 | + *target = val |
638 | +} |
639 | + |
640 | +// snapGetBool wraps SnapGet for bool types and verifies the snap var is valid |
641 | +func (c *Client) snapGetBool(key string, target *bool) { |
642 | + val, err := c.getter.SnapGet(key) |
643 | + if err != nil { |
644 | + return |
645 | + } |
646 | + if len(val) == 0 { |
647 | + log.Printf("== wifi-connect/configure error: key %s exists but has zero length", key) |
648 | + return |
649 | + } |
650 | + |
651 | + if val == "true" { |
652 | + *target = true |
653 | + } else { |
654 | + *target = false |
655 | + } |
656 | +} |
657 | + |
658 | +func main() { |
659 | + client := GetClient() |
660 | + preConfig := &daemon.PreConfig{} |
661 | + client.snapGetStr("wifi.security-passphrase", &preConfig.Passphrase) |
662 | + client.snapGetStr("portal.password", &preConfig.Password) |
663 | + client.snapGetBool("portal.no-operational", &preConfig.NoOperational) |
664 | + client.snapGetBool("portal.no-reset-creds", &preConfig.NoResetCreds) |
665 | + |
666 | + b, errJM := json.Marshal(preConfig) |
667 | + if errJM == nil { |
668 | + errWJ := ioutil.WriteFile(daemon.PreConfigFile, b, 0644) |
669 | + if errWJ != nil { |
670 | + log.Print("== wifi-connect/configure error:", errWJ) |
671 | + } |
672 | + } |
673 | +} |
674 | diff --git a/hooks/configure_test.go b/hooks/configure_test.go |
675 | new file mode 100644 |
676 | index 0000000..8aebd68 |
677 | --- /dev/null |
678 | +++ b/hooks/configure_test.go |
679 | @@ -0,0 +1,110 @@ |
680 | +// -*- Mode: Go; indent-tabs-mode: t -*- |
681 | + |
682 | +/* |
683 | + * Copyright (C) 2017 Canonical Ltd |
684 | + * |
685 | + * This program is free software: you can redistribute it and/or modify |
686 | + * it under the terms of the GNU General Public License version 3 as |
687 | + * published by the Free Software Foundation. |
688 | + * |
689 | + * This program is distributed in the hope that it will be useful, |
690 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
691 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
692 | + * GNU General Public License for more details. |
693 | + * |
694 | + * You should have received a copy of the GNU General Public License |
695 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
696 | + * |
697 | + */ |
698 | + |
699 | +package main |
700 | + |
701 | +import ( |
702 | + "fmt" |
703 | + "testing" |
704 | +) |
705 | + |
706 | +type mock0 struct{} |
707 | + |
708 | +func (mock *mock0) SnapGet(s string) (string, error) { |
709 | + return "snapgetreturn", nil |
710 | +} |
711 | + |
712 | +type Config struct { |
713 | + AString string |
714 | + ABool bool |
715 | +} |
716 | + |
717 | +func TestSnapGetStr0(t *testing.T) { |
718 | + c := ModClient(&mock0{}) |
719 | + config := &Config{} |
720 | + config.AString = "defaultstring" |
721 | + c.snapGetStr("key", &config.AString) |
722 | + if config.AString != "snapgetreturn" { |
723 | + t.Errorf("snapGetStr expected snapgetreturn but got %s", config.AString) |
724 | + } |
725 | +} |
726 | + |
727 | +type mock1 struct{} |
728 | + |
729 | +func (mock *mock1) SnapGet(s string) (string, error) { |
730 | + return "", fmt.Errorf("intentional error 1") |
731 | +} |
732 | + |
733 | +func TestSnapGetStr1(t *testing.T) { |
734 | + c := ModClient(&mock1{}) |
735 | + config := &Config{} |
736 | + config.AString = "defaultstring" |
737 | + c.snapGetStr("key", &config.AString) |
738 | + if config.AString != "defaultstring" { |
739 | + t.Errorf("snapGetStr expected defaultstring but got %s", config.AString) |
740 | + } |
741 | +} |
742 | + |
743 | +type mock2 struct{} |
744 | + |
745 | +func (mock *mock2) SnapGet(s string) (string, error) { |
746 | + return "true", nil |
747 | +} |
748 | + |
749 | +func TestSnapGetBool0(t *testing.T) { |
750 | + c := ModClient(&mock2{}) |
751 | + config := &Config{} |
752 | + config.ABool = false |
753 | + c.snapGetBool("key", &config.ABool) |
754 | + if !config.ABool { |
755 | + t.Errorf("snapGetBool should be true but is %t", config.ABool) |
756 | + } |
757 | +} |
758 | + |
759 | +type mock3 struct{} |
760 | + |
761 | +func (mock *mock3) SnapGet(s string) (string, error) { |
762 | + return "false", nil |
763 | +} |
764 | + |
765 | +func TestSnapGetBool1(t *testing.T) { |
766 | + c := ModClient(&mock3{}) |
767 | + config := &Config{} |
768 | + config.ABool = true |
769 | + c.snapGetBool("key", &config.ABool) |
770 | + if config.ABool { |
771 | + t.Errorf("snapGetBool should be false but is %t", config.ABool) |
772 | + } |
773 | +} |
774 | + |
775 | +type mock4 struct{} |
776 | + |
777 | +func (mock *mock4) SnapGet(s string) (string, error) { |
778 | + return "", nil |
779 | +} |
780 | + |
781 | +func TestSnapGetBool2(t *testing.T) { |
782 | + c := ModClient(&mock4{}) |
783 | + config := &Config{} |
784 | + config.ABool = true |
785 | + c.snapGetBool("key", &config.ABool) |
786 | + if !config.ABool { |
787 | + t.Errorf("snapGetBool should be true but is %t", config.ABool) |
788 | + } |
789 | +} |
790 | diff --git a/service/service.go b/service/service.go |
791 | index ec39f5f..c988f55 100644 |
792 | --- a/service/service.go |
793 | +++ b/service/service.go |
794 | @@ -29,16 +29,22 @@ import ( |
795 | ) |
796 | |
797 | func main() { |
798 | - |
799 | + c := netman.DefaultClient() |
800 | + cw := wifiap.DefaultClient() |
801 | client := daemon.GetClient() |
802 | - client.SetDefaults() |
803 | + |
804 | + config, err := daemon.LoadPreConfig() |
805 | + if err != nil { |
806 | + fmt.Println("== wifi-connect/daemon: preconfiguration error:", err) |
807 | + } |
808 | + err = client.SetDefaults(cw, config) |
809 | + if err != nil { |
810 | + fmt.Println("== wifi-connect/daemon: SetDetaults error:", err) |
811 | + } |
812 | first := true |
813 | client.SetWaitFlagPath(os.Getenv("SNAP_COMMON") + "/startingApConnect") |
814 | client.SetManualFlagPath(os.Getenv("SNAP_COMMON") + "/manualMode") |
815 | |
816 | - c := netman.DefaultClient() |
817 | - cw := wifiap.DefaultClient() |
818 | - |
819 | client.ManagementServerDown() |
820 | client.OperationalServerDown() |
821 | |
822 | @@ -95,7 +101,9 @@ func main() { |
823 | if client.GetPreviousState() == daemon.MANAGING { |
824 | client.ManagementServerDown() |
825 | } |
826 | - client.OperationalServerUp() |
827 | + if !config.NoOperational { |
828 | + client.OperationalServerUp() |
829 | + } |
830 | continue |
831 | } |
832 | |
833 | diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml |
834 | index 8bd7f04..c5fb86e 100644 |
835 | --- a/snap/snapcraft.yaml |
836 | +++ b/snap/snapcraft.yaml |
837 | @@ -1,8 +1,8 @@ |
838 | name: wifi-connect |
839 | -version: 0.10-1 |
840 | +version: 0.10-2-dev |
841 | summary: Connect your device to external wifi over temp wifi AP |
842 | description: | |
843 | - A solution to enable your device to connect to ian external |
844 | + A solution to enable your device to connect to an external |
845 | wifi AP using a temporary wifi AP the device puts up and then |
846 | opening its web portal. Note that wifi-connect daemon assumes |
847 | control of device network management and other management solutions |
848 | @@ -21,6 +21,10 @@ apps: |
849 | daemon: simple |
850 | plugs: [network-manager, control, network-bind] |
851 | |
852 | +hooks: |
853 | + configure: |
854 | + plugs: [network] |
855 | + |
856 | plugs: |
857 | control: |
858 | interface: content |
859 | @@ -46,9 +50,10 @@ parts: |
860 | export GOPATH=$PWD/../go |
861 | cd $GOPATH/src/launchpad.net/wifi-connect |
862 | ./run-checks all |
863 | + mkdir -p $SNAPCRAFT_PART_INSTALL/snap/hooks |
864 | + mv $SNAPCRAFT_PART_INSTALL/bin/hooks $SNAPCRAFT_PART_INSTALL/snap/hooks/configure |
865 | assets: |
866 | plugin: dump |
867 | source: . |
868 | stage: |
869 | - static |
870 | - |
871 | diff --git a/static/tests/pre-config0.json b/static/tests/pre-config0.json |
872 | new file mode 100644 |
873 | index 0000000..ad904cd |
874 | --- /dev/null |
875 | +++ b/static/tests/pre-config0.json |
876 | @@ -0,0 +1,7 @@ |
877 | +{ |
878 | + "wifi.security-passphrase": "abcdefghijklmnop", |
879 | + "portal.password": "qwerzxcv", |
880 | + "portal.no-operational": true, |
881 | + "portal.no-reset-creds": true |
882 | +} |
883 | + |
884 | diff --git a/static/tests/pre-config1.json b/static/tests/pre-config1.json |
885 | new file mode 100644 |
886 | index 0000000..f28c4ae |
887 | --- /dev/null |
888 | +++ b/static/tests/pre-config1.json |
889 | @@ -0,0 +1,5 @@ |
890 | +{ |
891 | + "portal.no-operational": false, |
892 | + "portal.no-reset-creds": false |
893 | +} |
894 | + |
895 | diff --git a/wifiap/wifiap.go b/wifiap/wifiap.go |
896 | index b609a55..faf0c15 100644 |
897 | --- a/wifiap/wifiap.go |
898 | +++ b/wifiap/wifiap.go |
899 | @@ -25,6 +25,11 @@ import ( |
900 | "time" |
901 | ) |
902 | |
903 | +const ( |
904 | + minPassphraseLen int = 8 |
905 | + maxPassphraseLen int = 63 |
906 | +) |
907 | + |
908 | // Client struct exposing wifi-ap operations |
909 | type Client struct { |
910 | restClient *RestClient |
911 | @@ -40,6 +45,16 @@ func DefaultClient() *Client { |
912 | return &Client{restClient: defaultRestClient()} |
913 | } |
914 | |
915 | +// Operations interface enables mock testing |
916 | +type Operations interface { |
917 | + Show() (map[string]interface{}, error) |
918 | + Enabled() (bool, error) |
919 | + Enable() error |
920 | + Disable() error |
921 | + SetSsid(string) error |
922 | + SetPassphrase(string) error |
923 | +} |
924 | + |
925 | func defaultServiceURI() string { |
926 | return fmt.Sprintf("http://unix%s", filepath.Join(versionURI, configurationURI)) |
927 | } |
928 | @@ -163,8 +178,11 @@ func (client *Client) SetSsid(ssid string) error { |
929 | |
930 | // SetPassphrase sets the credential to access the wifi ap |
931 | func (client *Client) SetPassphrase(passphrase string) error { |
932 | - if len(passphrase) < 13 { |
933 | - return fmt.Errorf("Passphrase must be at least 13 chars in length. Please try again") |
934 | + if len(passphrase) < minPassphraseLen { |
935 | + return fmt.Errorf("Passphrase must be at least 8 chars in length. Please try again") |
936 | + } |
937 | + if len(passphrase) > maxPassphraseLen { |
938 | + return fmt.Errorf("Passphrase must be less than 64 long. Please try again") |
939 | } |
940 | |
941 | params := map[string]string{ |
PASSED: Continuous integration, rev:710b0f6c013 a41be945d0c343e e556dc11fb986b /jenkins. canonical. com/system- enablement/ job/generic- build-snap/ 1689/ /jenkins. canonical. com/system- enablement/ job/generic- build-snap- worker/ 2239 /jenkins. canonical. com/system- enablement/ job/generic- update- snap-mp/ 1597/console /jenkins. canonical. com/system- enablement/ job/generic- test-snap/ 2602 /jenkins. canonical. com/system- enablement/ job/generic- cleanup- snap/1766/ console
https:/
Executed test runs:
SUCCESS: https:/
None: https:/
SUCCESS: https:/
None: https:/
Click here to trigger a rebuild: /jenkins. canonical. com/system- enablement/ job/generic- build-snap/ 1689/rebuild
https:/