diff -Nru juju-core-1.18.0/debian/changelog juju-core-1.18.1/debian/changelog --- juju-core-1.18.0/debian/changelog 2014-04-07 17:25:00.000000000 +0000 +++ juju-core-1.18.1/debian/changelog 2014-04-12 06:05:25.000000000 +0000 @@ -1,3 +1,18 @@ +juju-core (1.18.1-0ubuntu1) trusty; urgency=medium + + * New upstream point release, including fixes for: + - Upgrading juju 1.16.6 -> 1.18.x fails (LP: #1299802). + - Peer relation disappears during juju-upgrade (LP: #1303697). + - public-address of units changes to internal bridge post upgrade + (LP: #1303735). + - Unable to deploy local charms without series (LP: #1303880). + - juju scp no longer allows multiple extra arguments to be passed + (LP: #1306208). + - juju cannot downgrade to same major.minor version with earlier + patch number (LP: #1306296). + + -- James Page Sat, 12 Apr 2014 07:04:37 +0100 + juju-core (1.18.0-0ubuntu1) trusty; urgency=medium * New upstream release (LP: #1287147), including fixes for: diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/codereview.cfg juju-core-1.18.1/src/code.google.com/p/go.net/codereview.cfg --- juju-core-1.18.0/src/code.google.com/p/go.net/codereview.cfg 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/codereview.cfg 2014-04-11 09:53:46.000000000 +0000 @@ -1,2 +1,2 @@ -defaultcc: golang-dev@googlegroups.com +defaultcc: golang-codereviews@googlegroups.com contributors: http://go.googlecode.com/hg/CONTRIBUTORS diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/.hgignore juju-core-1.18.1/src/code.google.com/p/go.net/.hgignore --- juju-core-1.18.0/src/code.google.com/p/go.net/.hgignore 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/.hgignore 2014-04-11 09:53:46.000000000 +0000 @@ -1,2 +1,3 @@ +# Add no patterns to .hgignore except for files generated by the build. syntax:glob last-change diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/html/charset/charset.go juju-core-1.18.1/src/code.google.com/p/go.net/html/charset/charset.go --- juju-core-1.18.0/src/code.google.com/p/go.net/html/charset/charset.go 1970-01-01 00:00:00.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/html/charset/charset.go 2014-04-11 09:53:46.000000000 +0000 @@ -0,0 +1,227 @@ +// Package charset provides common text encodings for HTML documents. +// +// The mapping from encoding labels to encodings is defined at +// http://encoding.spec.whatwg.org. +package charset + +import ( + "bytes" + "io" + "mime" + "strings" + "unicode/utf8" + + "code.google.com/p/go.net/html" + "code.google.com/p/go.text/encoding" + "code.google.com/p/go.text/encoding/charmap" + "code.google.com/p/go.text/transform" +) + +// Lookup returns the encoding with the specified label, and its canonical +// name. It returns nil and the empty string if label is not one of the +// standard encodings for HTML. Matching is case-insensitive and ignores +// leading and trailing whitespace. +func Lookup(label string) (e encoding.Encoding, name string) { + label = strings.ToLower(strings.Trim(label, "\t\n\r\f ")) + enc := encodings[label] + return enc.e, enc.name +} + +// DetermineEncoding determines the encoding of an HTML document by examining +// up to the first 1024 bytes of content and the declared Content-Type. +// +// See http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#determining-the-character-encoding +func DetermineEncoding(content []byte, contentType string) (e encoding.Encoding, name string, certain bool) { + if len(content) > 1024 { + content = content[:1024] + } + + for _, b := range boms { + if bytes.HasPrefix(content, b.bom) { + e, name = Lookup(b.enc) + return e, name, true + } + } + + if _, params, err := mime.ParseMediaType(contentType); err == nil { + if cs, ok := params["charset"]; ok { + if e, name = Lookup(cs); e != nil { + return e, name, true + } + } + } + + if len(content) > 0 { + e, name = prescan(content) + if e != nil { + return e, name, false + } + } + + // Try to detect UTF-8. + // First eliminate any partial rune at the end. + for i := len(content) - 1; i >= 0 && i > len(content)-4; i-- { + b := content[i] + if b < 0x80 { + break + } + if utf8.RuneStart(b) { + content = content[:i] + break + } + } + hasHighBit := false + for _, c := range content { + if c >= 0x80 { + hasHighBit = true + break + } + } + if hasHighBit && utf8.Valid(content) { + return encoding.Nop, "utf-8", false + } + + // TODO: change default depending on user's locale? + return charmap.Windows1252, "windows-1252", false +} + +// NewReader returns an io.Reader that converts the content of r to UTF-8. +// It calls DetermineEncoding to find out what r's encoding is. +func NewReader(r io.Reader, contentType string) (io.Reader, error) { + preview := make([]byte, 1024) + n, err := io.ReadFull(r, preview) + switch { + case err == io.ErrUnexpectedEOF: + preview = preview[:n] + r = bytes.NewReader(preview) + case err != nil: + return nil, err + default: + r = io.MultiReader(bytes.NewReader(preview), r) + } + + if e, _, _ := DetermineEncoding(preview, contentType); e != encoding.Nop { + r = transform.NewReader(r, e.NewDecoder()) + } + return r, nil +} + +func prescan(content []byte) (e encoding.Encoding, name string) { + z := html.NewTokenizer(bytes.NewReader(content)) + for { + switch z.Next() { + case html.ErrorToken: + return nil, "" + + case html.StartTagToken, html.SelfClosingTagToken: + tagName, hasAttr := z.TagName() + if !bytes.Equal(tagName, []byte("meta")) { + continue + } + attrList := make(map[string]bool) + gotPragma := false + + const ( + dontKnow = iota + doNeedPragma + doNotNeedPragma + ) + needPragma := dontKnow + + name = "" + e = nil + for hasAttr { + var key, val []byte + key, val, hasAttr = z.TagAttr() + ks := string(key) + if attrList[ks] { + continue + } + attrList[ks] = true + for i, c := range val { + if 'A' <= c && c <= 'Z' { + val[i] = c + 0x20 + } + } + + switch ks { + case "http-equiv": + if bytes.Equal(val, []byte("content-type")) { + gotPragma = true + } + + case "content": + if e == nil { + name = fromMetaElement(string(val)) + if name != "" { + e, name = Lookup(name) + if e != nil { + needPragma = doNeedPragma + } + } + } + + case "charset": + e, name = Lookup(string(val)) + needPragma = doNotNeedPragma + } + } + + if needPragma == dontKnow || needPragma == doNeedPragma && !gotPragma { + continue + } + + if strings.HasPrefix(name, "utf-16") { + name = "utf-8" + e = encoding.Nop + } + + if e != nil { + return e, name + } + } + } +} + +func fromMetaElement(s string) string { + for s != "" { + csLoc := strings.Index(s, "charset") + if csLoc == -1 { + return "" + } + s = s[csLoc+len("charset"):] + s = strings.TrimLeft(s, " \t\n\f\r") + if !strings.HasPrefix(s, "=") { + continue + } + s = s[1:] + s = strings.TrimLeft(s, " \t\n\f\r") + if s == "" { + return "" + } + if q := s[0]; q == '"' || q == '\'' { + s = s[1:] + closeQuote := strings.IndexRune(s, rune(q)) + if closeQuote == -1 { + return "" + } + return s[:closeQuote] + } + + end := strings.IndexAny(s, "; \t\n\f\r") + if end == -1 { + end = len(s) + } + return s[:end] + } + return "" +} + +var boms = []struct { + bom []byte + enc string +}{ + {[]byte{0xfe, 0xff}, "utf-16be"}, + {[]byte{0xff, 0xfe}, "utf-16le"}, + {[]byte{0xef, 0xbb, 0xbf}, "utf-8"}, +} diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/html/charset/charset_test.go juju-core-1.18.1/src/code.google.com/p/go.net/html/charset/charset_test.go --- juju-core-1.18.0/src/code.google.com/p/go.net/html/charset/charset_test.go 1970-01-01 00:00:00.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/html/charset/charset_test.go 2014-04-11 09:53:46.000000000 +0000 @@ -0,0 +1,200 @@ +package charset + +import ( + "bytes" + "io/ioutil" + "strings" + "testing" + + "code.google.com/p/go.text/transform" +) + +func transformString(t transform.Transformer, s string) (string, error) { + r := transform.NewReader(strings.NewReader(s), t) + b, err := ioutil.ReadAll(r) + return string(b), err +} + +var testCases = []struct { + utf8, other, otherEncoding string +}{ + {"Résumé", "Résumé", "utf8"}, + {"Résumé", "R\xe9sum\xe9", "latin1"}, + {"これは漢字です。", "S0\x8c0o0\"oW[g0Y0\x020", "UTF-16LE"}, + {"これは漢字です。", "0S0\x8c0oo\"[W0g0Y0\x02", "UTF-16BE"}, + {"Hello, world", "Hello, world", "ASCII"}, + {"Gdańsk", "Gda\xf1sk", "ISO-8859-2"}, + {"Ââ Čč Đđ Ŋŋ Õõ Šš Žž Åå Ää", "\xc2\xe2 \xc8\xe8 \xa9\xb9 \xaf\xbf \xd5\xf5 \xaa\xba \xac\xbc \xc5\xe5 \xc4\xe4", "ISO-8859-10"}, + {"สำหรับ", "\xca\xd3\xcb\xc3\u047a", "ISO-8859-11"}, + {"latviešu", "latvie\xf0u", "ISO-8859-13"}, + {"Seònaid", "Se\xf2naid", "ISO-8859-14"}, + {"€1 is cheap", "\xa41 is cheap", "ISO-8859-15"}, + {"românește", "rom\xe2ne\xbate", "ISO-8859-16"}, + {"nutraĵo", "nutra\xbco", "ISO-8859-3"}, + {"Kalâdlit", "Kal\xe2dlit", "ISO-8859-4"}, + {"русский", "\xe0\xe3\xe1\xe1\xda\xd8\xd9", "ISO-8859-5"}, + {"ελληνικά", "\xe5\xeb\xeb\xe7\xed\xe9\xea\xdc", "ISO-8859-7"}, + {"Kağan", "Ka\xf0an", "ISO-8859-9"}, + {"Résumé", "R\x8esum\x8e", "macintosh"}, + {"Gdańsk", "Gda\xf1sk", "windows-1250"}, + {"русский", "\xf0\xf3\xf1\xf1\xea\xe8\xe9", "windows-1251"}, + {"Résumé", "R\xe9sum\xe9", "windows-1252"}, + {"ελληνικά", "\xe5\xeb\xeb\xe7\xed\xe9\xea\xdc", "windows-1253"}, + {"Kağan", "Ka\xf0an", "windows-1254"}, + {"עִבְרִית", "\xf2\xc4\xe1\xc0\xf8\xc4\xe9\xfa", "windows-1255"}, + {"العربية", "\xc7\xe1\xda\xd1\xc8\xed\xc9", "windows-1256"}, + {"latviešu", "latvie\xf0u", "windows-1257"}, + {"Việt", "Vi\xea\xf2t", "windows-1258"}, + {"สำหรับ", "\xca\xd3\xcb\xc3\u047a", "windows-874"}, + {"русский", "\xd2\xd5\xd3\xd3\xcb\xc9\xca", "KOI8-R"}, + {"українська", "\xd5\xcb\xd2\xc1\xa7\xce\xd3\xd8\xcb\xc1", "KOI8-U"}, + {"Hello 常用國字標準字體表", "Hello \xb1`\xa5\u03b0\xea\xa6r\xbc\u0437\u01e6r\xc5\xe9\xaa\xed", "big5"}, + {"Hello 常用國字標準字體表", "Hello \xb3\xa3\xd3\xc3\x87\xf8\xd7\xd6\x98\xcb\x9c\xca\xd7\xd6\xf3\x77\xb1\xed", "gbk"}, + {"Hello 常用國字標準字體表", "Hello \xb3\xa3\xd3\xc3\x87\xf8\xd7\xd6\x98\xcb\x9c\xca\xd7\xd6\xf3\x77\xb1\xed", "gb18030"}, + {"עִבְרִית", "\x81\x30\xfb\x30\x81\x30\xf6\x34\x81\x30\xf9\x33\x81\x30\xf6\x30\x81\x30\xfb\x36\x81\x30\xf6\x34\x81\x30\xfa\x31\x81\x30\xfb\x38", "gb18030"}, + {"㧯", "\x82\x31\x89\x38", "gb18030"}, + {"これは漢字です。", "\x82\xb1\x82\xea\x82\xcd\x8a\xbf\x8e\x9a\x82\xc5\x82\xb7\x81B", "SJIS"}, + {"Hello, 世界!", "Hello, \x90\xa2\x8aE!", "SJIS"}, + {"イウエオカ", "\xb2\xb3\xb4\xb5\xb6", "SJIS"}, + {"これは漢字です。", "\xa4\xb3\xa4\xec\xa4\u03f4\xc1\xbb\xfa\xa4\u01e4\xb9\xa1\xa3", "EUC-JP"}, + {"Hello, 世界!", "Hello, \x1b$B@$3&\x1b(B!", "ISO-2022-JP"}, + {"네이트 | 즐거움의 시작, 슈파스(Spaβ) NATE", "\xb3\xd7\xc0\xcc\xc6\xae | \xc1\xf1\xb0\xc5\xbf\xf2\xc0\xc7 \xbd\xc3\xc0\xdb, \xbd\xb4\xc6\xc4\xbd\xba(Spa\xa5\xe2) NATE", "EUC-KR"}, +} + +func TestDecode(t *testing.T) { + for _, tc := range testCases { + e, _ := Lookup(tc.otherEncoding) + if e == nil { + t.Errorf("%s: not found", tc.otherEncoding) + continue + } + s, err := transformString(e.NewDecoder(), tc.other) + if err != nil { + t.Errorf("%s: decode %q: %v", tc.otherEncoding, tc.other, err) + continue + } + if s != tc.utf8 { + t.Errorf("%s: got %q, want %q", tc.otherEncoding, s, tc.utf8) + } + } +} + +func TestEncode(t *testing.T) { + for _, tc := range testCases { + e, _ := Lookup(tc.otherEncoding) + if e == nil { + t.Errorf("%s: not found", tc.otherEncoding) + continue + } + s, err := transformString(e.NewEncoder(), tc.utf8) + if err != nil { + t.Errorf("%s: encode %q: %s", tc.otherEncoding, tc.utf8, err) + continue + } + if s != tc.other { + t.Errorf("%s: got %q, want %q", tc.otherEncoding, s, tc.other) + } + } +} + +// TestNames verifies that you can pass an encoding's name to Lookup and get +// the same encoding back (except for "replacement"). +func TestNames(t *testing.T) { + for _, e := range encodings { + if e.name == "replacement" { + continue + } + _, got := Lookup(e.name) + if got != e.name { + t.Errorf("got %q, want %q", got, e.name) + continue + } + } +} + +var sniffTestCases = []struct { + filename, declared, want string +}{ + {"HTTP-charset.html", "text/html; charset=iso-8859-15", "iso-8859-15"}, + {"UTF-16LE-BOM.html", "", "utf-16le"}, + {"UTF-16BE-BOM.html", "", "utf-16be"}, + {"meta-content-attribute.html", "text/html", "iso-8859-15"}, + {"meta-charset-attribute.html", "text/html", "iso-8859-15"}, + {"No-encoding-declaration.html", "text/html", "utf-8"}, + {"HTTP-vs-UTF-8-BOM.html", "text/html; charset=iso-8859-15", "utf-8"}, + {"HTTP-vs-meta-content.html", "text/html; charset=iso-8859-15", "iso-8859-15"}, + {"HTTP-vs-meta-charset.html", "text/html; charset=iso-8859-15", "iso-8859-15"}, + {"UTF-8-BOM-vs-meta-content.html", "text/html", "utf-8"}, + {"UTF-8-BOM-vs-meta-charset.html", "text/html", "utf-8"}, +} + +func TestSniff(t *testing.T) { + for _, tc := range sniffTestCases { + content, err := ioutil.ReadFile("testdata/" + tc.filename) + if err != nil { + t.Errorf("%s: error reading file: %v", tc.filename, err) + continue + } + + _, name, _ := DetermineEncoding(content, tc.declared) + if name != tc.want { + t.Errorf("%s: got %q, want %q", tc.filename, name, tc.want) + continue + } + } +} + +func TestReader(t *testing.T) { + for _, tc := range sniffTestCases { + content, err := ioutil.ReadFile("testdata/" + tc.filename) + if err != nil { + t.Errorf("%s: error reading file: %v", tc.filename, err) + continue + } + + r, err := NewReader(bytes.NewReader(content), tc.declared) + if err != nil { + t.Errorf("%s: error creating reader: %v", tc.filename, err) + continue + } + + got, err := ioutil.ReadAll(r) + if err != nil { + t.Errorf("%s: error reading from charset.NewReader: %v", tc.filename, err) + continue + } + + e, _ := Lookup(tc.want) + want, err := ioutil.ReadAll(transform.NewReader(bytes.NewReader(content), e.NewDecoder())) + if err != nil { + t.Errorf("%s: error decoding with hard-coded charset name: %v", tc.filename, err) + continue + } + + if !bytes.Equal(got, want) { + t.Errorf("%s: got %q, want %q", tc.filename, got, want) + continue + } + } +} + +var metaTestCases = []struct { + meta, want string +}{ + {"", ""}, + {"text/html", ""}, + {"text/html; charset utf-8", ""}, + {"text/html; charset=latin-2", "latin-2"}, + {"text/html; charset; charset = utf-8", "utf-8"}, + {`charset="big5"`, "big5"}, + {"charset='shift_jis'", "shift_jis"}, +} + +func TestFromMeta(t *testing.T) { + for _, tc := range metaTestCases { + got := fromMetaElement(tc.meta) + if got != tc.want { + t.Errorf("%q: got %q, want %q", tc.meta, got, tc.want) + } + } +} diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/html/charset/gen.go juju-core-1.18.1/src/code.google.com/p/go.net/html/charset/gen.go --- juju-core-1.18.0/src/code.google.com/p/go.net/html/charset/gen.go 1970-01-01 00:00:00.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/html/charset/gen.go 2014-04-11 09:53:46.000000000 +0000 @@ -0,0 +1,107 @@ +// +build ignore + +package main + +// Download http://encoding.spec.whatwg.org/encodings.json and use it to +// generate table.go. + +import ( + "encoding/json" + "fmt" + "log" + "net/http" + "strings" +) + +type enc struct { + Name string + Labels []string +} + +type group struct { + Encodings []enc + Heading string +} + +const specURL = "http://encoding.spec.whatwg.org/encodings.json" + +func main() { + resp, err := http.Get(specURL) + if err != nil { + log.Fatalf("error fetching %s: %s", specURL, err) + } + if resp.StatusCode != 200 { + log.Fatalf("error fetching %s: HTTP status %s", specURL, resp.Status) + } + defer resp.Body.Close() + + var groups []group + d := json.NewDecoder(resp.Body) + err = d.Decode(&groups) + if err != nil { + log.Fatalf("error reading encodings.json: %s", err) + } + + fmt.Println("// generated by go run gen.go; DO NOT EDIT") + fmt.Println() + fmt.Println("package charset") + fmt.Println() + + fmt.Println("import (") + fmt.Println(`"code.google.com/p/go.text/encoding"`) + for _, pkg := range []string{"charmap", "japanese", "korean", "simplifiedchinese", "traditionalchinese", "unicode"} { + fmt.Printf("\"code.google.com/p/go.text/encoding/%s\"\n", pkg) + } + fmt.Println(")") + fmt.Println() + + fmt.Println("var encodings = map[string]struct{e encoding.Encoding; name string} {") + for _, g := range groups { + for _, e := range g.Encodings { + goName, ok := miscNames[e.Name] + if !ok { + for k, v := range prefixes { + if strings.HasPrefix(e.Name, k) { + goName = v + e.Name[len(k):] + break + } + } + if goName == "" { + log.Fatalf("unrecognized encoding name: %s", e.Name) + } + } + + for _, label := range e.Labels { + fmt.Printf("%q: {%s, %q},\n", label, goName, e.Name) + } + } + } + fmt.Println("}") +} + +var prefixes = map[string]string{ + "iso-8859-": "charmap.ISO8859_", + "windows-": "charmap.Windows", +} + +var miscNames = map[string]string{ + "utf-8": "encoding.Nop", + "ibm866": "charmap.CodePage866", + "iso-8859-8-i": "charmap.ISO8859_8", + "koi8-r": "charmap.KOI8R", + "koi8-u": "charmap.KOI8U", + "macintosh": "charmap.Macintosh", + "x-mac-cyrillic": "charmap.MacintoshCyrillic", + "gbk": "simplifiedchinese.GBK", + "gb18030": "simplifiedchinese.GB18030", + "hz-gb-2312": "simplifiedchinese.HZGB2312", + "big5": "traditionalchinese.Big5", + "euc-jp": "japanese.EUCJP", + "iso-2022-jp": "japanese.ISO2022JP", + "shift_jis": "japanese.ShiftJIS", + "euc-kr": "korean.EUCKR", + "replacement": "encoding.Replacement", + "utf-16be": "unicode.UTF16(unicode.BigEndian, unicode.IgnoreBOM)", + "utf-16le": "unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM)", + "x-user-defined": "charmap.XUserDefined", +} diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/html/charset/table.go juju-core-1.18.1/src/code.google.com/p/go.net/html/charset/table.go --- juju-core-1.18.0/src/code.google.com/p/go.net/html/charset/table.go 1970-01-01 00:00:00.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/html/charset/table.go 2014-04-11 09:53:46.000000000 +0000 @@ -0,0 +1,235 @@ +// generated by go run gen.go; DO NOT EDIT + +package charset + +import ( + "code.google.com/p/go.text/encoding" + "code.google.com/p/go.text/encoding/charmap" + "code.google.com/p/go.text/encoding/japanese" + "code.google.com/p/go.text/encoding/korean" + "code.google.com/p/go.text/encoding/simplifiedchinese" + "code.google.com/p/go.text/encoding/traditionalchinese" + "code.google.com/p/go.text/encoding/unicode" +) + +var encodings = map[string]struct { + e encoding.Encoding + name string +}{ + "unicode-1-1-utf-8": {encoding.Nop, "utf-8"}, + "utf-8": {encoding.Nop, "utf-8"}, + "utf8": {encoding.Nop, "utf-8"}, + "866": {charmap.CodePage866, "ibm866"}, + "cp866": {charmap.CodePage866, "ibm866"}, + "csibm866": {charmap.CodePage866, "ibm866"}, + "ibm866": {charmap.CodePage866, "ibm866"}, + "csisolatin2": {charmap.ISO8859_2, "iso-8859-2"}, + "iso-8859-2": {charmap.ISO8859_2, "iso-8859-2"}, + "iso-ir-101": {charmap.ISO8859_2, "iso-8859-2"}, + "iso8859-2": {charmap.ISO8859_2, "iso-8859-2"}, + "iso88592": {charmap.ISO8859_2, "iso-8859-2"}, + "iso_8859-2": {charmap.ISO8859_2, "iso-8859-2"}, + "iso_8859-2:1987": {charmap.ISO8859_2, "iso-8859-2"}, + "l2": {charmap.ISO8859_2, "iso-8859-2"}, + "latin2": {charmap.ISO8859_2, "iso-8859-2"}, + "csisolatin3": {charmap.ISO8859_3, "iso-8859-3"}, + "iso-8859-3": {charmap.ISO8859_3, "iso-8859-3"}, + "iso-ir-109": {charmap.ISO8859_3, "iso-8859-3"}, + "iso8859-3": {charmap.ISO8859_3, "iso-8859-3"}, + "iso88593": {charmap.ISO8859_3, "iso-8859-3"}, + "iso_8859-3": {charmap.ISO8859_3, "iso-8859-3"}, + "iso_8859-3:1988": {charmap.ISO8859_3, "iso-8859-3"}, + "l3": {charmap.ISO8859_3, "iso-8859-3"}, + "latin3": {charmap.ISO8859_3, "iso-8859-3"}, + "csisolatin4": {charmap.ISO8859_4, "iso-8859-4"}, + "iso-8859-4": {charmap.ISO8859_4, "iso-8859-4"}, + "iso-ir-110": {charmap.ISO8859_4, "iso-8859-4"}, + "iso8859-4": {charmap.ISO8859_4, "iso-8859-4"}, + "iso88594": {charmap.ISO8859_4, "iso-8859-4"}, + "iso_8859-4": {charmap.ISO8859_4, "iso-8859-4"}, + "iso_8859-4:1988": {charmap.ISO8859_4, "iso-8859-4"}, + "l4": {charmap.ISO8859_4, "iso-8859-4"}, + "latin4": {charmap.ISO8859_4, "iso-8859-4"}, + "csisolatincyrillic": {charmap.ISO8859_5, "iso-8859-5"}, + "cyrillic": {charmap.ISO8859_5, "iso-8859-5"}, + "iso-8859-5": {charmap.ISO8859_5, "iso-8859-5"}, + "iso-ir-144": {charmap.ISO8859_5, "iso-8859-5"}, + "iso8859-5": {charmap.ISO8859_5, "iso-8859-5"}, + "iso88595": {charmap.ISO8859_5, "iso-8859-5"}, + "iso_8859-5": {charmap.ISO8859_5, "iso-8859-5"}, + "iso_8859-5:1988": {charmap.ISO8859_5, "iso-8859-5"}, + "arabic": {charmap.ISO8859_6, "iso-8859-6"}, + "asmo-708": {charmap.ISO8859_6, "iso-8859-6"}, + "csiso88596e": {charmap.ISO8859_6, "iso-8859-6"}, + "csiso88596i": {charmap.ISO8859_6, "iso-8859-6"}, + "csisolatinarabic": {charmap.ISO8859_6, "iso-8859-6"}, + "ecma-114": {charmap.ISO8859_6, "iso-8859-6"}, + "iso-8859-6": {charmap.ISO8859_6, "iso-8859-6"}, + "iso-8859-6-e": {charmap.ISO8859_6, "iso-8859-6"}, + "iso-8859-6-i": {charmap.ISO8859_6, "iso-8859-6"}, + "iso-ir-127": {charmap.ISO8859_6, "iso-8859-6"}, + "iso8859-6": {charmap.ISO8859_6, "iso-8859-6"}, + "iso88596": {charmap.ISO8859_6, "iso-8859-6"}, + "iso_8859-6": {charmap.ISO8859_6, "iso-8859-6"}, + "iso_8859-6:1987": {charmap.ISO8859_6, "iso-8859-6"}, + "csisolatingreek": {charmap.ISO8859_7, "iso-8859-7"}, + "ecma-118": {charmap.ISO8859_7, "iso-8859-7"}, + "elot_928": {charmap.ISO8859_7, "iso-8859-7"}, + "greek": {charmap.ISO8859_7, "iso-8859-7"}, + "greek8": {charmap.ISO8859_7, "iso-8859-7"}, + "iso-8859-7": {charmap.ISO8859_7, "iso-8859-7"}, + "iso-ir-126": {charmap.ISO8859_7, "iso-8859-7"}, + "iso8859-7": {charmap.ISO8859_7, "iso-8859-7"}, + "iso88597": {charmap.ISO8859_7, "iso-8859-7"}, + "iso_8859-7": {charmap.ISO8859_7, "iso-8859-7"}, + "iso_8859-7:1987": {charmap.ISO8859_7, "iso-8859-7"}, + "sun_eu_greek": {charmap.ISO8859_7, "iso-8859-7"}, + "csiso88598e": {charmap.ISO8859_8, "iso-8859-8"}, + "csisolatinhebrew": {charmap.ISO8859_8, "iso-8859-8"}, + "hebrew": {charmap.ISO8859_8, "iso-8859-8"}, + "iso-8859-8": {charmap.ISO8859_8, "iso-8859-8"}, + "iso-8859-8-e": {charmap.ISO8859_8, "iso-8859-8"}, + "iso-ir-138": {charmap.ISO8859_8, "iso-8859-8"}, + "iso8859-8": {charmap.ISO8859_8, "iso-8859-8"}, + "iso88598": {charmap.ISO8859_8, "iso-8859-8"}, + "iso_8859-8": {charmap.ISO8859_8, "iso-8859-8"}, + "iso_8859-8:1988": {charmap.ISO8859_8, "iso-8859-8"}, + "visual": {charmap.ISO8859_8, "iso-8859-8"}, + "csiso88598i": {charmap.ISO8859_8, "iso-8859-8-i"}, + "iso-8859-8-i": {charmap.ISO8859_8, "iso-8859-8-i"}, + "logical": {charmap.ISO8859_8, "iso-8859-8-i"}, + "csisolatin6": {charmap.ISO8859_10, "iso-8859-10"}, + "iso-8859-10": {charmap.ISO8859_10, "iso-8859-10"}, + "iso-ir-157": {charmap.ISO8859_10, "iso-8859-10"}, + "iso8859-10": {charmap.ISO8859_10, "iso-8859-10"}, + "iso885910": {charmap.ISO8859_10, "iso-8859-10"}, + "l6": {charmap.ISO8859_10, "iso-8859-10"}, + "latin6": {charmap.ISO8859_10, "iso-8859-10"}, + "iso-8859-13": {charmap.ISO8859_13, "iso-8859-13"}, + "iso8859-13": {charmap.ISO8859_13, "iso-8859-13"}, + "iso885913": {charmap.ISO8859_13, "iso-8859-13"}, + "iso-8859-14": {charmap.ISO8859_14, "iso-8859-14"}, + "iso8859-14": {charmap.ISO8859_14, "iso-8859-14"}, + "iso885914": {charmap.ISO8859_14, "iso-8859-14"}, + "csisolatin9": {charmap.ISO8859_15, "iso-8859-15"}, + "iso-8859-15": {charmap.ISO8859_15, "iso-8859-15"}, + "iso8859-15": {charmap.ISO8859_15, "iso-8859-15"}, + "iso885915": {charmap.ISO8859_15, "iso-8859-15"}, + "iso_8859-15": {charmap.ISO8859_15, "iso-8859-15"}, + "l9": {charmap.ISO8859_15, "iso-8859-15"}, + "iso-8859-16": {charmap.ISO8859_16, "iso-8859-16"}, + "cskoi8r": {charmap.KOI8R, "koi8-r"}, + "koi": {charmap.KOI8R, "koi8-r"}, + "koi8": {charmap.KOI8R, "koi8-r"}, + "koi8-r": {charmap.KOI8R, "koi8-r"}, + "koi8_r": {charmap.KOI8R, "koi8-r"}, + "koi8-u": {charmap.KOI8U, "koi8-u"}, + "csmacintosh": {charmap.Macintosh, "macintosh"}, + "mac": {charmap.Macintosh, "macintosh"}, + "macintosh": {charmap.Macintosh, "macintosh"}, + "x-mac-roman": {charmap.Macintosh, "macintosh"}, + "dos-874": {charmap.Windows874, "windows-874"}, + "iso-8859-11": {charmap.Windows874, "windows-874"}, + "iso8859-11": {charmap.Windows874, "windows-874"}, + "iso885911": {charmap.Windows874, "windows-874"}, + "tis-620": {charmap.Windows874, "windows-874"}, + "windows-874": {charmap.Windows874, "windows-874"}, + "cp1250": {charmap.Windows1250, "windows-1250"}, + "windows-1250": {charmap.Windows1250, "windows-1250"}, + "x-cp1250": {charmap.Windows1250, "windows-1250"}, + "cp1251": {charmap.Windows1251, "windows-1251"}, + "windows-1251": {charmap.Windows1251, "windows-1251"}, + "x-cp1251": {charmap.Windows1251, "windows-1251"}, + "ansi_x3.4-1968": {charmap.Windows1252, "windows-1252"}, + "ascii": {charmap.Windows1252, "windows-1252"}, + "cp1252": {charmap.Windows1252, "windows-1252"}, + "cp819": {charmap.Windows1252, "windows-1252"}, + "csisolatin1": {charmap.Windows1252, "windows-1252"}, + "ibm819": {charmap.Windows1252, "windows-1252"}, + "iso-8859-1": {charmap.Windows1252, "windows-1252"}, + "iso-ir-100": {charmap.Windows1252, "windows-1252"}, + "iso8859-1": {charmap.Windows1252, "windows-1252"}, + "iso88591": {charmap.Windows1252, "windows-1252"}, + "iso_8859-1": {charmap.Windows1252, "windows-1252"}, + "iso_8859-1:1987": {charmap.Windows1252, "windows-1252"}, + "l1": {charmap.Windows1252, "windows-1252"}, + "latin1": {charmap.Windows1252, "windows-1252"}, + "us-ascii": {charmap.Windows1252, "windows-1252"}, + "windows-1252": {charmap.Windows1252, "windows-1252"}, + "x-cp1252": {charmap.Windows1252, "windows-1252"}, + "cp1253": {charmap.Windows1253, "windows-1253"}, + "windows-1253": {charmap.Windows1253, "windows-1253"}, + "x-cp1253": {charmap.Windows1253, "windows-1253"}, + "cp1254": {charmap.Windows1254, "windows-1254"}, + "csisolatin5": {charmap.Windows1254, "windows-1254"}, + "iso-8859-9": {charmap.Windows1254, "windows-1254"}, + "iso-ir-148": {charmap.Windows1254, "windows-1254"}, + "iso8859-9": {charmap.Windows1254, "windows-1254"}, + "iso88599": {charmap.Windows1254, "windows-1254"}, + "iso_8859-9": {charmap.Windows1254, "windows-1254"}, + "iso_8859-9:1989": {charmap.Windows1254, "windows-1254"}, + "l5": {charmap.Windows1254, "windows-1254"}, + "latin5": {charmap.Windows1254, "windows-1254"}, + "windows-1254": {charmap.Windows1254, "windows-1254"}, + "x-cp1254": {charmap.Windows1254, "windows-1254"}, + "cp1255": {charmap.Windows1255, "windows-1255"}, + "windows-1255": {charmap.Windows1255, "windows-1255"}, + "x-cp1255": {charmap.Windows1255, "windows-1255"}, + "cp1256": {charmap.Windows1256, "windows-1256"}, + "windows-1256": {charmap.Windows1256, "windows-1256"}, + "x-cp1256": {charmap.Windows1256, "windows-1256"}, + "cp1257": {charmap.Windows1257, "windows-1257"}, + "windows-1257": {charmap.Windows1257, "windows-1257"}, + "x-cp1257": {charmap.Windows1257, "windows-1257"}, + "cp1258": {charmap.Windows1258, "windows-1258"}, + "windows-1258": {charmap.Windows1258, "windows-1258"}, + "x-cp1258": {charmap.Windows1258, "windows-1258"}, + "x-mac-cyrillic": {charmap.MacintoshCyrillic, "x-mac-cyrillic"}, + "x-mac-ukrainian": {charmap.MacintoshCyrillic, "x-mac-cyrillic"}, + "chinese": {simplifiedchinese.GBK, "gbk"}, + "csgb2312": {simplifiedchinese.GBK, "gbk"}, + "csiso58gb231280": {simplifiedchinese.GBK, "gbk"}, + "gb2312": {simplifiedchinese.GBK, "gbk"}, + "gb_2312": {simplifiedchinese.GBK, "gbk"}, + "gb_2312-80": {simplifiedchinese.GBK, "gbk"}, + "gbk": {simplifiedchinese.GBK, "gbk"}, + "iso-ir-58": {simplifiedchinese.GBK, "gbk"}, + "x-gbk": {simplifiedchinese.GBK, "gbk"}, + "gb18030": {simplifiedchinese.GB18030, "gb18030"}, + "hz-gb-2312": {simplifiedchinese.HZGB2312, "hz-gb-2312"}, + "big5": {traditionalchinese.Big5, "big5"}, + "big5-hkscs": {traditionalchinese.Big5, "big5"}, + "cn-big5": {traditionalchinese.Big5, "big5"}, + "csbig5": {traditionalchinese.Big5, "big5"}, + "x-x-big5": {traditionalchinese.Big5, "big5"}, + "cseucpkdfmtjapanese": {japanese.EUCJP, "euc-jp"}, + "euc-jp": {japanese.EUCJP, "euc-jp"}, + "x-euc-jp": {japanese.EUCJP, "euc-jp"}, + "csiso2022jp": {japanese.ISO2022JP, "iso-2022-jp"}, + "iso-2022-jp": {japanese.ISO2022JP, "iso-2022-jp"}, + "csshiftjis": {japanese.ShiftJIS, "shift_jis"}, + "ms_kanji": {japanese.ShiftJIS, "shift_jis"}, + "shift-jis": {japanese.ShiftJIS, "shift_jis"}, + "shift_jis": {japanese.ShiftJIS, "shift_jis"}, + "sjis": {japanese.ShiftJIS, "shift_jis"}, + "windows-31j": {japanese.ShiftJIS, "shift_jis"}, + "x-sjis": {japanese.ShiftJIS, "shift_jis"}, + "cseuckr": {korean.EUCKR, "euc-kr"}, + "csksc56011987": {korean.EUCKR, "euc-kr"}, + "euc-kr": {korean.EUCKR, "euc-kr"}, + "iso-ir-149": {korean.EUCKR, "euc-kr"}, + "korean": {korean.EUCKR, "euc-kr"}, + "ks_c_5601-1987": {korean.EUCKR, "euc-kr"}, + "ks_c_5601-1989": {korean.EUCKR, "euc-kr"}, + "ksc5601": {korean.EUCKR, "euc-kr"}, + "ksc_5601": {korean.EUCKR, "euc-kr"}, + "windows-949": {korean.EUCKR, "euc-kr"}, + "csiso2022kr": {encoding.Replacement, "replacement"}, + "iso-2022-kr": {encoding.Replacement, "replacement"}, + "iso-2022-cn": {encoding.Replacement, "replacement"}, + "iso-2022-cn-ext": {encoding.Replacement, "replacement"}, + "utf-16be": {unicode.UTF16(unicode.BigEndian, unicode.IgnoreBOM), "utf-16be"}, + "utf-16": {unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM), "utf-16le"}, + "utf-16le": {unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM), "utf-16le"}, + "x-user-defined": {charmap.XUserDefined, "x-user-defined"}, +} diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/html/charset/testdata/HTTP-charset.html juju-core-1.18.1/src/code.google.com/p/go.net/html/charset/testdata/HTTP-charset.html --- juju-core-1.18.0/src/code.google.com/p/go.net/html/charset/testdata/HTTP-charset.html 1970-01-01 00:00:00.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/html/charset/testdata/HTTP-charset.html 2014-04-11 09:53:46.000000000 +0000 @@ -0,0 +1,48 @@ + + + + HTTP charset + + + + + + + + + + + +

HTTP charset

+ + +
+ + +
 
+ + + + + +
+

The character encoding of a page can be set using the HTTP header charset declaration.

+

The test contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector .test div.ÜÀÚ. This matches the sequence of bytes above when they are interpreted as ISO 8859-15. If the class name matches the selector then the test will pass.

The only character encoding declaration for this HTML file is in the HTTP header, which sets the encoding to ISO 8859-15.

+
+
+
Next test
HTML5
+

the-input-byte-stream-001
Result summary & related tests
Detailed results for this test
Link to spec

+
Assumptions:
  • The default encoding for the browser you are testing is not set to ISO 8859-15.
  • +
  • The test is read from a server that supports HTTP.
+
+ + + + + + diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/html/charset/testdata/HTTP-vs-meta-charset.html juju-core-1.18.1/src/code.google.com/p/go.net/html/charset/testdata/HTTP-vs-meta-charset.html --- juju-core-1.18.0/src/code.google.com/p/go.net/html/charset/testdata/HTTP-vs-meta-charset.html 1970-01-01 00:00:00.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/html/charset/testdata/HTTP-vs-meta-charset.html 2014-04-11 09:53:46.000000000 +0000 @@ -0,0 +1,49 @@ + + + + HTTP vs meta charset + + + + + + + + + + + +

HTTP vs meta charset

+ + +
+ + +
 
+ + + + + +
+

The HTTP header has a higher precedence than an encoding declaration in a meta charset attribute.

+

The HTTP header attempts to set the character encoding to ISO 8859-15. The page contains an encoding declaration in a meta charset attribute that attempts to set the character encoding to ISO 8859-1.

The test contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector .test div.ÜÀÚ. This matches the sequence of bytes above when they are interpreted as ISO 8859-15. If the class name matches the selector then the test will pass.

+
+
+
Next test
HTML5
+

the-input-byte-stream-018
Result summary & related tests
Detailed results for this test
Link to spec

+
Assumptions:
  • The default encoding for the browser you are testing is not set to ISO 8859-15.
  • +
  • The test is read from a server that supports HTTP.
+
+ + + + + + diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/html/charset/testdata/HTTP-vs-meta-content.html juju-core-1.18.1/src/code.google.com/p/go.net/html/charset/testdata/HTTP-vs-meta-content.html --- juju-core-1.18.0/src/code.google.com/p/go.net/html/charset/testdata/HTTP-vs-meta-content.html 1970-01-01 00:00:00.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/html/charset/testdata/HTTP-vs-meta-content.html 2014-04-11 09:53:46.000000000 +0000 @@ -0,0 +1,49 @@ + + + + HTTP vs meta content + + + + + + + + + + + +

HTTP vs meta content

+ + +
+ + +
 
+ + + + + +
+

The HTTP header has a higher precedence than an encoding declaration in a meta content attribute.

+

The HTTP header attempts to set the character encoding to ISO 8859-15. The page contains an encoding declaration in a meta content attribute that attempts to set the character encoding to ISO 8859-1.

The test contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector .test div.ÜÀÚ. This matches the sequence of bytes above when they are interpreted as ISO 8859-15. If the class name matches the selector then the test will pass.

+
+
+
Next test
HTML5
+

the-input-byte-stream-016
Result summary & related tests
Detailed results for this test
Link to spec

+
Assumptions:
  • The default encoding for the browser you are testing is not set to ISO 8859-15.
  • +
  • The test is read from a server that supports HTTP.
+
+ + + + + + diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/html/charset/testdata/HTTP-vs-UTF-8-BOM.html juju-core-1.18.1/src/code.google.com/p/go.net/html/charset/testdata/HTTP-vs-UTF-8-BOM.html --- juju-core-1.18.0/src/code.google.com/p/go.net/html/charset/testdata/HTTP-vs-UTF-8-BOM.html 1970-01-01 00:00:00.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/html/charset/testdata/HTTP-vs-UTF-8-BOM.html 2014-04-11 09:53:46.000000000 +0000 @@ -0,0 +1,48 @@ + + + + HTTP vs UTF-8 BOM + + + + + + + + + + + +

HTTP vs UTF-8 BOM

+ + +
+ + +
 
+ + + + + +
+

A character encoding set in the HTTP header has lower precedence than the UTF-8 signature.

+

The HTTP header attempts to set the character encoding to ISO 8859-15. The page starts with a UTF-8 signature.

The test contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector .test div.ýäè. This matches the sequence of bytes above when they are interpreted as UTF-8. If the class name matches the selector then the test will pass.

If the test is unsuccessful, the characters  should appear at the top of the page. These represent the bytes that make up the UTF-8 signature when encountered in the ISO 8859-15 encoding.

+
+
+
Next test
HTML5
+

the-input-byte-stream-034
Result summary & related tests
Detailed results for this test
Link to spec

+
Assumptions:
  • The default encoding for the browser you are testing is not set to ISO 8859-15.
  • +
  • The test is read from a server that supports HTTP.
+
+ + + + + + diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/html/charset/testdata/meta-charset-attribute.html juju-core-1.18.1/src/code.google.com/p/go.net/html/charset/testdata/meta-charset-attribute.html --- juju-core-1.18.0/src/code.google.com/p/go.net/html/charset/testdata/meta-charset-attribute.html 1970-01-01 00:00:00.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/html/charset/testdata/meta-charset-attribute.html 2014-04-11 09:53:46.000000000 +0000 @@ -0,0 +1,48 @@ + + + + meta charset attribute + + + + + + + + + + + +

meta charset attribute

+ + +
+ + +
 
+ + + + + +
+

The character encoding of the page can be set by a meta element with charset attribute.

+

The only character encoding declaration for this HTML file is in the charset attribute of the meta element, which declares the encoding to be ISO 8859-15.

The test contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector .test div.ÜÀÚ. This matches the sequence of bytes above when they are interpreted as ISO 8859-15. If the class name matches the selector then the test will pass.

+
+
+
Next test
HTML5
+

the-input-byte-stream-009
Result summary & related tests
Detailed results for this test
Link to spec

+
Assumptions:
  • The default encoding for the browser you are testing is not set to ISO 8859-15.
  • +
  • The test is read from a server that supports HTTP.
+
+ + + + + + diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/html/charset/testdata/meta-content-attribute.html juju-core-1.18.1/src/code.google.com/p/go.net/html/charset/testdata/meta-content-attribute.html --- juju-core-1.18.0/src/code.google.com/p/go.net/html/charset/testdata/meta-content-attribute.html 1970-01-01 00:00:00.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/html/charset/testdata/meta-content-attribute.html 2014-04-11 09:53:46.000000000 +0000 @@ -0,0 +1,48 @@ + + + + meta content attribute + + + + + + + + + + + +

meta content attribute

+ + +
+ + +
 
+ + + + + +
+

The character encoding of the page can be set by a meta element with http-equiv and content attributes.

+

The only character encoding declaration for this HTML file is in the content attribute of the meta element, which declares the encoding to be ISO 8859-15.

The test contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector .test div.ÜÀÚ. This matches the sequence of bytes above when they are interpreted as ISO 8859-15. If the class name matches the selector then the test will pass.

+
+
+
Next test
HTML5
+

the-input-byte-stream-007
Result summary & related tests
Detailed results for this test
Link to spec

+
Assumptions:
  • The default encoding for the browser you are testing is not set to ISO 8859-15.
  • +
  • The test is read from a server that supports HTTP.
+
+ + + + + + diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/html/charset/testdata/No-encoding-declaration.html juju-core-1.18.1/src/code.google.com/p/go.net/html/charset/testdata/No-encoding-declaration.html --- juju-core-1.18.0/src/code.google.com/p/go.net/html/charset/testdata/No-encoding-declaration.html 1970-01-01 00:00:00.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/html/charset/testdata/No-encoding-declaration.html 2014-04-11 09:53:46.000000000 +0000 @@ -0,0 +1,47 @@ + + + + No encoding declaration + + + + + + + + + + + +

No encoding declaration

+ + +
+ + +
 
+ + + + + +
+

A page with no encoding information in HTTP, BOM, XML declaration or meta element will be treated as UTF-8.

+

The test on this page contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector .test div.ýäè. This matches the sequence of bytes above when they are interpreted as UTF-8. If the class name matches the selector then the test will pass.

+
+
+
Next test
HTML5
+

the-input-byte-stream-015
Result summary & related tests
Detailed results for this test
Link to spec

+
Assumptions:
  • The test is read from a server that supports HTTP.
+
+ + + + + + diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/html/charset/testdata/README juju-core-1.18.1/src/code.google.com/p/go.net/html/charset/testdata/README --- juju-core-1.18.0/src/code.google.com/p/go.net/html/charset/testdata/README 1970-01-01 00:00:00.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/html/charset/testdata/README 2014-04-11 09:53:46.000000000 +0000 @@ -0,0 +1 @@ +These test cases come from http://www.w3.org/International/tests/html5/the-input-byte-stream/results-basics Binary files /tmp/WoHS2SRfgP/juju-core-1.18.0/src/code.google.com/p/go.net/html/charset/testdata/UTF-16BE-BOM.html and /tmp/STPWf8JUdO/juju-core-1.18.1/src/code.google.com/p/go.net/html/charset/testdata/UTF-16BE-BOM.html differ Binary files /tmp/WoHS2SRfgP/juju-core-1.18.0/src/code.google.com/p/go.net/html/charset/testdata/UTF-16LE-BOM.html and /tmp/STPWf8JUdO/juju-core-1.18.1/src/code.google.com/p/go.net/html/charset/testdata/UTF-16LE-BOM.html differ diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/html/charset/testdata/UTF-8-BOM-vs-meta-charset.html juju-core-1.18.1/src/code.google.com/p/go.net/html/charset/testdata/UTF-8-BOM-vs-meta-charset.html --- juju-core-1.18.0/src/code.google.com/p/go.net/html/charset/testdata/UTF-8-BOM-vs-meta-charset.html 1970-01-01 00:00:00.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/html/charset/testdata/UTF-8-BOM-vs-meta-charset.html 2014-04-11 09:53:46.000000000 +0000 @@ -0,0 +1,49 @@ + + + + UTF-8 BOM vs meta charset + + + + + + + + + + + +

UTF-8 BOM vs meta charset

+ + +
+ + +
 
+ + + + + +
+

A page with a UTF-8 BOM will be recognized as UTF-8 even if the meta charset attribute declares a different encoding.

+

The page contains an encoding declaration in a meta charset attribute that attempts to set the character encoding to ISO 8859-15, but the file starts with a UTF-8 signature.

The test contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector .test div.ýäè. This matches the sequence of bytes above when they are interpreted as UTF-8. If the class name matches the selector then the test will pass.

+
+
+
Next test
HTML5
+

the-input-byte-stream-038
Result summary & related tests
Detailed results for this test
Link to spec

+
Assumptions:
  • The default encoding for the browser you are testing is not set to ISO 8859-15.
  • +
  • The test is read from a server that supports HTTP.
+
+ + + + + + diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/html/charset/testdata/UTF-8-BOM-vs-meta-content.html juju-core-1.18.1/src/code.google.com/p/go.net/html/charset/testdata/UTF-8-BOM-vs-meta-content.html --- juju-core-1.18.0/src/code.google.com/p/go.net/html/charset/testdata/UTF-8-BOM-vs-meta-content.html 1970-01-01 00:00:00.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/html/charset/testdata/UTF-8-BOM-vs-meta-content.html 2014-04-11 09:53:46.000000000 +0000 @@ -0,0 +1,48 @@ + + + + UTF-8 BOM vs meta content + + + + + + + + + + + +

UTF-8 BOM vs meta content

+ + +
+ + +
 
+ + + + + +
+

A page with a UTF-8 BOM will be recognized as UTF-8 even if the meta content attribute declares a different encoding.

+

The page contains an encoding declaration in a meta content attribute that attempts to set the character encoding to ISO 8859-15, but the file starts with a UTF-8 signature.

The test contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector .test div.ýäè. This matches the sequence of bytes above when they are interpreted as UTF-8. If the class name matches the selector then the test will pass.

+
+
+
Next test
HTML5
+

the-input-byte-stream-037
Result summary & related tests
Detailed results for this test
Link to spec

+
Assumptions:
  • The default encoding for the browser you are testing is not set to ISO 8859-15.
  • +
  • The test is read from a server that supports HTTP.
+
+ + + + + + diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/html/token.go juju-core-1.18.1/src/code.google.com/p/go.net/html/token.go --- juju-core-1.18.0/src/code.google.com/p/go.net/html/token.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/html/token.go 2014-04-11 09:53:46.000000000 +0000 @@ -6,6 +6,7 @@ import ( "bytes" + "errors" "io" "strconv" "strings" @@ -33,6 +34,9 @@ DoctypeToken ) +// ErrBufferExceeded means that the buffering limit was exceeded. +var ErrBufferExceeded = errors.New("max buffer exceeded") + // String returns a string representation of the TokenType. func (t TokenType) String() string { switch t { @@ -142,6 +146,8 @@ // buf[raw.end:] is buffered input that will yield future tokens. raw span buf []byte + // maxBuf limits the data buffered in buf. A value of 0 means unlimited. + maxBuf int // buf[data.start:data.end] holds the raw bytes of the current token's data: // a text token's text, a tag token's tag name, etc. data span @@ -273,9 +279,18 @@ } x := z.buf[z.raw.end] z.raw.end++ + if z.maxBuf > 0 && z.raw.end-z.raw.start >= z.maxBuf { + z.err = ErrBufferExceeded + return 0 + } return x } +// Buffered returns a slice containing data buffered but not yet tokenized. +func (z *Tokenizer) Buffered() []byte { + return z.buf[z.raw.end:] +} + // readAtLeastOneByte wraps an io.Reader so that reading cannot return (0, nil). // It returns io.ErrNoProgress if the underlying r.Read method returns (0, nil) // too many times in succession. @@ -734,7 +749,6 @@ brackets = 0 } } - panic("unreachable") } // startTagIn returns whether the start tag in z.buf[z.data.start:z.data.end] @@ -934,13 +948,13 @@ // Next scans the next token and returns its type. func (z *Tokenizer) Next() TokenType { + z.raw.start = z.raw.end + z.data.start = z.raw.end + z.data.end = z.raw.end if z.err != nil { z.tt = ErrorToken return z.tt } - z.raw.start = z.raw.end - z.data.start = z.raw.end - z.data.end = z.raw.end if z.rawTag != "" { if z.rawTag == "plaintext" { // Read everything up to EOF. @@ -1010,12 +1024,11 @@ break loop } if c == '>' { - // "" does not generate a token at all. + // "" does not generate a token at all. Generate an empty comment + // to allow passthrough clients to pick up the data using Raw. // Reset the tokenizer state and start again. - z.raw.start = z.raw.end - z.data.start = z.raw.end - z.data.end = z.raw.end - continue loop + z.tt = CommentToken + return z.tt } if 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' { z.readTag(false) @@ -1169,6 +1182,12 @@ return t } +// SetMaxBuf sets a limit on the amount of data buffered during tokenization. +// A value of 0 means unlimited. +func (z *Tokenizer) SetMaxBuf(n int) { + z.maxBuf = n +} + // NewTokenizer returns a new HTML Tokenizer for the given Reader. // The input is assumed to be UTF-8 encoded. func NewTokenizer(r io.Reader) *Tokenizer { diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/html/token_test.go juju-core-1.18.1/src/code.google.com/p/go.net/html/token_test.go --- juju-core-1.18.0/src/code.google.com/p/go.net/html/token_test.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/html/token_test.go 2014-04-11 09:53:46.000000000 +0000 @@ -63,12 +63,12 @@ { "not a tag #2", "", - "", + "", }, { "not a tag #3", "ab", - "a$b", + "a$$b", }, { "not a tag #4", @@ -468,6 +468,80 @@ } } } + +func TestMaxBuffer(t *testing.T) { + // Exceeding the maximum buffer size generates ErrBufferExceeded. + z := NewTokenizer(strings.NewReader("<" + strings.Repeat("t", 10))) + z.SetMaxBuf(5) + tt := z.Next() + if got, want := tt, ErrorToken; got != want { + t.Fatalf("token type: got: %v want: %v", got, want) + } + if got, want := z.Err(), ErrBufferExceeded; got != want { + t.Errorf("error type: got: %v want: %v", got, want) + } + if got, want := string(z.Raw()), "title"` - Note string `xml:"registry>note"` - Records []protocolRecord `xml:"registry>record"` -} - -type protocolRecord struct { - Value string `xml:"value"` - Name string `xml:"name"` - Descr string `xml:"description"` + XMLName xml.Name `xml:"registry"` + Title string `xml:"title"` + Updated string `xml:"updated"` + RegTitle string `xml:"registry>title"` + Note string `xml:"registry>note"` + Records []struct { + Value string `xml:"value"` + Name string `xml:"name"` + Descr string `xml:"description"` + } `xml:"registry>record"` } type canonProtocolRecord struct { diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv4/gentest.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv4/gentest.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv4/gentest.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv4/gentest.go 2014-04-11 09:53:46.000000000 +0000 @@ -39,7 +39,7 @@ func main() { var bb bytes.Buffer - fmt.Fprintf(&bb, "// go run gentv.go\n") + fmt.Fprintf(&bb, "// go run gentest.go\n") fmt.Fprintf(&bb, "// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT\n\n") fmt.Fprintf(&bb, "package ipv4_test\n\n") for _, r := range registries { @@ -85,18 +85,19 @@ } type dscpRegistry struct { - XMLName xml.Name `xml:"registry"` - Title string `xml:"title"` - Updated string `xml:"updated"` - Note string `xml:"note"` - RegTitle string `xml:"registry>title"` - PoolRecords []dscpRecord `xml:"registry>record"` - Records []dscpRecord `xml:"registry>registry>record"` -} - -type dscpRecord struct { - Name string `xml:"name"` - Space string `xml:"space"` + XMLName xml.Name `xml:"registry"` + Title string `xml:"title"` + Updated string `xml:"updated"` + Note string `xml:"note"` + RegTitle string `xml:"registry>title"` + PoolRecords []struct { + Name string `xml:"name"` + Space string `xml:"space"` + } `xml:"registry>record"` + Records []struct { + Name string `xml:"name"` + Space string `xml:"space"` + } `xml:"registry>registry>record"` } type canonDSCPRecord struct { @@ -145,17 +146,15 @@ } type tosTCByte struct { - XMLName xml.Name `xml:"registry"` - Title string `xml:"title"` - Updated string `xml:"updated"` - Note string `xml:"note"` - RegTitle string `xml:"registry>title"` - Records []tosTCByteRecord `xml:"registry>record"` -} - -type tosTCByteRecord struct { - Binary string `xml:"binary"` - Keyword string `xml:"keyword"` + XMLName xml.Name `xml:"registry"` + Title string `xml:"title"` + Updated string `xml:"updated"` + Note string `xml:"note"` + RegTitle string `xml:"registry>title"` + Records []struct { + Binary string `xml:"binary"` + Keyword string `xml:"keyword"` + } `xml:"registry>record"` } type canonTOSTCByteRecord struct { diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv4/header.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv4/header.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv4/header.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv4/header.go 2014-04-11 09:53:46.000000000 +0000 @@ -36,41 +36,47 @@ maxHeaderLen = 60 // sensible default, revisit if later RFCs define new usage of version and header length fields ) -type headerField int +const ( + posTOS = 1 // type-of-service + posTotalLen = 2 // packet total length + posID = 4 // identification + posFragOff = 6 // fragment offset + posTTL = 8 // time-to-live + posProtocol = 9 // next protocol + posChecksum = 10 // checksum + posSrc = 12 // source address + posDst = 16 // destination address +) + +type HeaderFlags int const ( - posTOS headerField = 1 // type-of-service - posTotalLen = 2 // packet total length - posID = 4 // identification - posFragOff = 6 // fragment offset - posTTL = 8 // time-to-live - posProtocol = 9 // next protocol - posChecksum = 10 // checksum - posSrc = 12 // source address - posDst = 16 // destination address + MoreFragments HeaderFlags = 1 << iota // more fragments flag + DontFragment // don't fragment flag ) // A Header represents an IPv4 header. type Header struct { - Version int // protocol version - Len int // header length - TOS int // type-of-service - TotalLen int // packet total length - ID int // identification - FragOff int // fragment offset - TTL int // time-to-live - Protocol int // next protocol - Checksum int // checksum - Src net.IP // source address - Dst net.IP // destination address - Options []byte // options, extension headers + Version int // protocol version + Len int // header length + TOS int // type-of-service + TotalLen int // packet total length + ID int // identification + Flags HeaderFlags // flags + FragOff int // fragment offset + TTL int // time-to-live + Protocol int // next protocol + Checksum int // checksum + Src net.IP // source address + Dst net.IP // destination address + Options []byte // options, extension headers } func (h *Header) String() string { if h == nil { return "" } - return fmt.Sprintf("ver: %v, hdrlen: %v, tos: %#x, totallen: %v, id: %#x, fragoff: %#x, ttl: %v, proto: %v, cksum: %#x, src: %v, dst: %v", h.Version, h.Len, h.TOS, h.TotalLen, h.ID, h.FragOff, h.TTL, h.Protocol, h.Checksum, h.Src, h.Dst) + return fmt.Sprintf("ver: %v, hdrlen: %v, tos: %#x, totallen: %v, id: %#x, flags: %#x, fragoff: %#x, ttl: %v, proto: %v, cksum: %#x, src: %v, dst: %v", h.Version, h.Len, h.TOS, h.TotalLen, h.ID, h.Flags, h.FragOff, h.TTL, h.Protocol, h.Checksum, h.Src, h.Dst) } // Please refer to the online manual; IP(4) on Darwin, FreeBSD and @@ -89,12 +95,13 @@ b := make([]byte, hdrlen) b[0] = byte(Version<<4 | (hdrlen >> 2 & 0x0f)) b[posTOS] = byte(h.TOS) + flagsAndFragOff := (h.FragOff & 0x1fff) | int(h.Flags<<13) if supportsNewIPInput { b[posTotalLen], b[posTotalLen+1] = byte(h.TotalLen>>8), byte(h.TotalLen) - b[posFragOff], b[posFragOff+1] = byte(h.FragOff>>8), byte(h.FragOff) + b[posFragOff], b[posFragOff+1] = byte(flagsAndFragOff>>8), byte(flagsAndFragOff) } else { *(*uint16)(unsafe.Pointer(&b[posTotalLen : posTotalLen+1][0])) = uint16(h.TotalLen) - *(*uint16)(unsafe.Pointer(&b[posFragOff : posFragOff+1][0])) = uint16(h.FragOff) + *(*uint16)(unsafe.Pointer(&b[posFragOff : posFragOff+1][0])) = uint16(flagsAndFragOff) } b[posID], b[posID+1] = byte(h.ID>>8), byte(h.ID) b[posTTL] = byte(h.TTL) @@ -114,6 +121,9 @@ return b, nil } +// See http://www.freebsd.org/doc/en/books/porters-handbook/freebsd-versions.html. +var freebsdVersion uint32 + // ParseHeader parses b as an IPv4 header. func ParseHeader(b []byte) (*Header, error) { if len(b) < HeaderLen { @@ -132,9 +142,13 @@ h.FragOff = int(b[posFragOff])<<8 | int(b[posFragOff+1]) } else { h.TotalLen = int(*(*uint16)(unsafe.Pointer(&b[posTotalLen : posTotalLen+1][0]))) - h.TotalLen += hdrlen + if runtime.GOOS != "freebsd" || freebsdVersion < 1000000 { + h.TotalLen += hdrlen + } h.FragOff = int(*(*uint16)(unsafe.Pointer(&b[posFragOff : posFragOff+1][0]))) } + h.Flags = HeaderFlags(h.FragOff&0xe000) >> 13 + h.FragOff = h.FragOff & 0x1fff h.ID = int(b[posID])<<8 | int(b[posID+1]) h.TTL = int(b[posTTL]) h.Protocol = int(b[posProtocol]) diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv4/header_test.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv4/header_test.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv4/header_test.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv4/header_test.go 2014-04-11 09:53:46.000000000 +0000 @@ -2,11 +2,10 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package ipv4_test +package ipv4 import ( "bytes" - "code.google.com/p/go.net/ipv4" "net" "reflect" "runtime" @@ -14,30 +13,37 @@ ) var ( - wireHeaderFromKernel = [ipv4.HeaderLen]byte{ + wireHeaderFromKernel = [HeaderLen]byte{ 0x45, 0x01, 0xbe, 0xef, - 0xca, 0xfe, 0x05, 0xdc, + 0xca, 0xfe, 0x45, 0xdc, 0xff, 0x01, 0xde, 0xad, 172, 16, 254, 254, 192, 168, 0, 1, } - wireHeaderToKernel = [ipv4.HeaderLen]byte{ + wireHeaderToKernel = [HeaderLen]byte{ 0x45, 0x01, 0xbe, 0xef, - 0xca, 0xfe, 0x05, 0xdc, + 0xca, 0xfe, 0x45, 0xdc, 0xff, 0x01, 0xde, 0xad, 172, 16, 254, 254, 192, 168, 0, 1, } - wireHeaderFromTradBSDKernel = [ipv4.HeaderLen]byte{ + wireHeaderFromTradBSDKernel = [HeaderLen]byte{ 0x45, 0x01, 0xdb, 0xbe, - 0xca, 0xfe, 0xdc, 0x05, + 0xca, 0xfe, 0xdc, 0x45, 0xff, 0x01, 0xde, 0xad, 172, 16, 254, 254, 192, 168, 0, 1, } - wireHeaderToTradBSDKernel = [ipv4.HeaderLen]byte{ + wireHeaderFromFreeBSD10Kernel = [HeaderLen]byte{ 0x45, 0x01, 0xef, 0xbe, - 0xca, 0xfe, 0xdc, 0x05, + 0xca, 0xfe, 0xdc, 0x45, + 0xff, 0x01, 0xde, 0xad, + 172, 16, 254, 254, + 192, 168, 0, 1, + } + wireHeaderToTradBSDKernel = [HeaderLen]byte{ + 0x45, 0x01, 0xef, 0xbe, + 0xca, 0xfe, 0xdc, 0x45, 0xff, 0x01, 0xde, 0xad, 172, 16, 254, 254, 192, 168, 0, 1, @@ -45,12 +51,13 @@ // TODO(mikio): Add platform dependent wire header formats when // we support new platforms. - testHeader = &ipv4.Header{ - Version: ipv4.Version, - Len: ipv4.HeaderLen, + testHeader = &Header{ + Version: Version, + Len: HeaderLen, TOS: 1, TotalLen: 0xbeef, ID: 0xcafe, + Flags: DontFragment, FragOff: 1500, TTL: 255, Protocol: 1, @@ -66,10 +73,9 @@ t.Fatalf("ipv4.Header.Marshal failed: %v", err) } var wh []byte - switch runtime.GOOS { - case "linux", "openbsd": + if supportsNewIPInput { wh = wireHeaderToKernel[:] - default: + } else { wh = wireHeaderToTradBSDKernel[:] } if !bytes.Equal(b, wh) { @@ -79,13 +85,16 @@ func TestParseHeader(t *testing.T) { var wh []byte - switch runtime.GOOS { - case "linux", "openbsd": + if supportsNewIPInput { wh = wireHeaderFromKernel[:] - default: - wh = wireHeaderFromTradBSDKernel[:] + } else { + if runtime.GOOS == "freebsd" && freebsdVersion >= 1000000 { + wh = wireHeaderFromFreeBSD10Kernel[:] + } else { + wh = wireHeaderFromTradBSDKernel[:] + } } - h, err := ipv4.ParseHeader(wh) + h, err := ParseHeader(wh) if err != nil { t.Fatalf("ipv4.ParseHeader failed: %v", err) } diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv4/helper.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv4/helper.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv4/helper.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv4/helper.go 2014-04-11 09:53:46.000000000 +0000 @@ -10,6 +10,7 @@ ) var ( + errOpNoSupport = errors.New("operation not supported") errNoSuchInterface = errors.New("no such interface") errNoSuchMulticastInterface = errors.New("no such multicast interface") ) diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv4/helper_plan9.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv4/helper_plan9.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv4/helper_plan9.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv4/helper_plan9.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,27 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ipv4 - -import "syscall" - -func (c *genericOpt) sysfd() (int, error) { - // TODO(mikio): Implement this - return 0, syscall.EPLAN9 -} - -func (c *dgramOpt) sysfd() (int, error) { - // TODO(mikio): Implement this - return 0, syscall.EPLAN9 -} - -func (c *payloadHandler) sysfd() (int, error) { - // TODO(mikio): Implement this - return 0, syscall.EPLAN9 -} - -func (c *packetHandler) sysfd() (int, error) { - // TODO(mikio): Implement this - return 0, syscall.EPLAN9 -} diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv4/helper_stub.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv4/helper_stub.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv4/helper_stub.go 1970-01-01 00:00:00.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv4/helper_stub.go 2014-04-11 09:53:46.000000000 +0000 @@ -0,0 +1,27 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build dragonfly plan9 solaris + +package ipv4 + +func (c *genericOpt) sysfd() (int, error) { + // TODO(mikio): Implement this + return 0, errOpNoSupport +} + +func (c *dgramOpt) sysfd() (int, error) { + // TODO(mikio): Implement this + return 0, errOpNoSupport +} + +func (c *payloadHandler) sysfd() (int, error) { + // TODO(mikio): Implement this + return 0, errOpNoSupport +} + +func (c *packetHandler) sysfd() (int, error) { + // TODO(mikio): Implement this + return 0, errOpNoSupport +} diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv4/iana_test.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv4/iana_test.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv4/iana_test.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv4/iana_test.go 2014-04-11 09:53:46.000000000 +0000 @@ -1,9 +1,9 @@ -// go run gentv.go +// go run gentest.go // GENERATED BY THE COMMAND ABOVE; DO NOT EDIT package ipv4_test -// Differentiated Services Field Codepoints, Updated: 2010-05-11 +// Differentiated Services Field Codepoints (DSCP), Updated: 2013-06-25 const ( DiffServCS0 = 0x0 // CS0 DiffServCS1 = 0x20 // CS1 diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv4/mocktransponder_test.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv4/mocktransponder_test.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv4/mocktransponder_test.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv4/mocktransponder_test.go 2014-04-11 09:53:46.000000000 +0000 @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin freebsd linux netbsd openbsd - package ipv4_test import ( @@ -75,6 +73,10 @@ return b } +func isUnicast(ip net.IP) bool { + return ip.To4() != nil && (ip.IsLoopback() || ip.IsLinkLocalUnicast() || ip.IsGlobalUnicast()) +} + // LoopbackInterface returns a logical network interface for loopback // tests. func loopbackInterface() *net.Interface { @@ -83,8 +85,24 @@ return nil } for _, ifi := range ift { - if ifi.Flags&net.FlagLoopback != 0 { - return &ifi + if ifi.Flags&net.FlagLoopback == 0 || ifi.Flags&net.FlagUp == 0 { + continue + } + ifat, err := ifi.Addrs() + if err != nil { + continue + } + for _, ifa := range ifat { + switch ifa := ifa.(type) { + case *net.IPAddr: + if isUnicast(ifa.IP) { + return &ifi + } + case *net.IPNet: + if isUnicast(ifa.IP) { + return &ifi + } + } } } return nil @@ -94,31 +112,24 @@ // enabled network interface. It also returns a unicast IPv4 address // that can be used for listening on ifi. func isMulticastAvailable(ifi *net.Interface) (net.IP, bool) { - if ifi.Flags&net.FlagUp == 0 || ifi.Flags&net.FlagMulticast == 0 { + if ifi == nil || ifi.Flags&net.FlagUp == 0 || ifi.Flags&net.FlagMulticast == 0 { return nil, false } ifat, err := ifi.Addrs() if err != nil { return nil, false } - if len(ifat) == 0 { - return nil, false - } - var ip net.IP for _, ifa := range ifat { - switch v := ifa.(type) { + switch ifa := ifa.(type) { case *net.IPAddr: - ip = v.IP + if isUnicast(ifa.IP) { + return ifa.IP, true + } case *net.IPNet: - ip = v.IP - default: - continue - } - if ip.To4() == nil { - ip = nil - continue + if isUnicast(ifa.IP) { + return ifa.IP, true + } } - break } - return ip, true + return nil, false } diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv4/multicastlistener_test.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv4/multicastlistener_test.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv4/multicastlistener_test.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv4/multicastlistener_test.go 2014-04-11 09:53:46.000000000 +0000 @@ -2,41 +2,41 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin freebsd linux netbsd openbsd - package ipv4_test import ( "code.google.com/p/go.net/ipv4" "net" "os" + "runtime" "testing" ) -var udpMultipleGroupListenerTests = []struct { - gaddr *net.UDPAddr -}{ - {&net.UDPAddr{IP: net.IPv4(224, 0, 0, 249)}}, // see RFC 4727 - {&net.UDPAddr{IP: net.IPv4(224, 0, 0, 250)}}, // see RFC 4727 - {&net.UDPAddr{IP: net.IPv4(224, 0, 0, 254)}}, // see RFC 4727 +var udpMultipleGroupListenerTests = []net.Addr{ + &net.UDPAddr{IP: net.IPv4(224, 0, 0, 249)}, // see RFC 4727 + &net.UDPAddr{IP: net.IPv4(224, 0, 0, 250)}, + &net.UDPAddr{IP: net.IPv4(224, 0, 0, 254)}, } -func TestUDPSingleConnWithMultipleGroupListeners(t *testing.T) { +func TestUDPSinglePacketConnWithMultipleGroupListeners(t *testing.T) { + switch runtime.GOOS { + case "plan9", "windows": + t.Skipf("not supported on %q", runtime.GOOS) + } if testing.Short() || !*testExternal { t.Skip("to avoid external network") } - for _, tt := range udpMultipleGroupListenerTests { - // listen to a wildcard address with no reusable port - c, err := net.ListenPacket("udp4", "0.0.0.0:0") + for _, gaddr := range udpMultipleGroupListenerTests { + c, err := net.ListenPacket("udp4", "0.0.0.0:0") // wildcard address with no reusable port if err != nil { t.Fatalf("net.ListenPacket failed: %v", err) } defer c.Close() p := ipv4.NewPacketConn(c) - var mift []*net.Interface + ift, err := net.Interfaces() if err != nil { t.Fatalf("net.Interfaces failed: %v", err) @@ -45,34 +45,36 @@ if _, ok := isMulticastAvailable(&ifi); !ok { continue } - if err := p.JoinGroup(&ifi, tt.gaddr); err != nil { - t.Fatalf("ipv4.PacketConn.JoinGroup %v on %v failed: %v", tt.gaddr, ifi, err) + if err := p.JoinGroup(&ifi, gaddr); err != nil { + t.Fatalf("ipv4.PacketConn.JoinGroup %v on %v failed: %v", gaddr, ifi, err) } mift = append(mift, &ift[i]) } for _, ifi := range mift { - if err := p.LeaveGroup(ifi, tt.gaddr); err != nil { - t.Fatalf("ipv4.PacketConn.LeaveGroup %v on %v failed: %v", tt.gaddr, ifi, err) + if err := p.LeaveGroup(ifi, gaddr); err != nil { + t.Fatalf("ipv4.PacketConn.LeaveGroup %v on %v failed: %v", gaddr, ifi, err) } } } } -func TestUDPMultipleConnWithMultipleGroupListeners(t *testing.T) { +func TestUDPMultiplePacketConnWithMultipleGroupListeners(t *testing.T) { + switch runtime.GOOS { + case "plan9", "windows": + t.Skipf("not supported on %q", runtime.GOOS) + } if testing.Short() || !*testExternal { t.Skip("to avoid external network") } - for _, tt := range udpMultipleGroupListenerTests { - // listen to a group address, actually a wildcard address - // with reusable port - c1, err := net.ListenPacket("udp4", "224.0.0.0:1024") // see RFC 4727 + for _, gaddr := range udpMultipleGroupListenerTests { + c1, err := net.ListenPacket("udp4", "224.0.0.0:1024") // wildcard address with reusable port if err != nil { t.Fatalf("net.ListenPacket failed: %v", err) } defer c1.Close() - c2, err := net.ListenPacket("udp4", "224.0.0.0:1024") // see RFC 4727 + c2, err := net.ListenPacket("udp4", "224.0.0.0:1024") // wildcard address with reusable port if err != nil { t.Fatalf("net.ListenPacket failed: %v", err) } @@ -81,8 +83,8 @@ var ps [2]*ipv4.PacketConn ps[0] = ipv4.NewPacketConn(c1) ps[1] = ipv4.NewPacketConn(c2) - var mift []*net.Interface + ift, err := net.Interfaces() if err != nil { t.Fatalf("net.Interfaces failed: %v", err) @@ -92,105 +94,115 @@ continue } for _, p := range ps { - if err := p.JoinGroup(&ifi, tt.gaddr); err != nil { - t.Fatalf("ipv4.PacketConn.JoinGroup %v on %v failed: %v", tt.gaddr, ifi, err) + if err := p.JoinGroup(&ifi, gaddr); err != nil { + t.Fatalf("ipv4.PacketConn.JoinGroup %v on %v failed: %v", gaddr, ifi, err) } } mift = append(mift, &ift[i]) } for _, ifi := range mift { for _, p := range ps { - if err := p.LeaveGroup(ifi, tt.gaddr); err != nil { - t.Fatalf("ipv4.PacketConn.LeaveGroup %v on %v failed: %v", tt.gaddr, ifi, err) + if err := p.LeaveGroup(ifi, gaddr); err != nil { + t.Fatalf("ipv4.PacketConn.LeaveGroup %v on %v failed: %v", gaddr, ifi, err) } } } } } -func TestIPSingleConnWithSingleGroupListener(t *testing.T) { +func TestUDPPerInterfaceSinglePacketConnWithSingleGroupListener(t *testing.T) { + switch runtime.GOOS { + case "plan9", "windows": + t.Skipf("not supported on %q", runtime.GOOS) + } if testing.Short() || !*testExternal { t.Skip("to avoid external network") } - if os.Getuid() != 0 { - t.Skip("must be root") - } - // listen to a wildcard address - c, err := net.ListenPacket("ip4:icmp", "0.0.0.0") - if err != nil { - t.Fatalf("net.ListenPacket failed: %v", err) - } - defer c.Close() - - r, err := ipv4.NewRawConn(c) - if err != nil { - t.Fatalf("ipv4.RawConn failed: %v", err) + gaddr := net.IPAddr{IP: net.IPv4(224, 0, 0, 254)} // see RFC 4727 + type ml struct { + c *ipv4.PacketConn + ifi *net.Interface } + var mlt []*ml - gaddr := &net.IPAddr{IP: net.IPv4(224, 0, 0, 254)} // see RFC 4727 - var mift []*net.Interface ift, err := net.Interfaces() if err != nil { t.Fatalf("net.Interfaces failed: %v", err) } for i, ifi := range ift { - if _, ok := isMulticastAvailable(&ifi); !ok { + ip, ok := isMulticastAvailable(&ifi) + if !ok { continue } - if err := r.JoinGroup(&ifi, gaddr); err != nil { - t.Fatalf("ipv4.RawConn.JoinGroup on %v failed: %v", ifi, err) + c, err := net.ListenPacket("udp4", ip.String()+":"+"1024") // unicast address with non-reusable port + if err != nil { + t.Fatalf("net.ListenPacket with %v failed: %v", ip, err) } - mift = append(mift, &ift[i]) + defer c.Close() + p := ipv4.NewPacketConn(c) + if err := p.JoinGroup(&ifi, &gaddr); err != nil { + t.Fatalf("ipv4.PacketConn.JoinGroup on %v failed: %v", ifi, err) + } + mlt = append(mlt, &ml{p, &ift[i]}) } - for _, ifi := range mift { - if err := r.LeaveGroup(ifi, gaddr); err != nil { - t.Fatalf("ipv4.RawConn.LeaveGroup on %v failed: %v", ifi, err) + for _, m := range mlt { + if err := m.c.LeaveGroup(m.ifi, &gaddr); err != nil { + t.Fatalf("ipv4.PacketConn.LeaveGroup on %v failed: %v", m.ifi, err) } } } -func TestUDPPerInterfaceSingleConnWithSingleGroupListener(t *testing.T) { +func TestIPSingleRawConnWithSingleGroupListener(t *testing.T) { + switch runtime.GOOS { + case "plan9", "windows": + t.Skipf("not supported on %q", runtime.GOOS) + } if testing.Short() || !*testExternal { t.Skip("to avoid external network") } + if os.Getuid() != 0 { + t.Skip("must be root") + } - gaddr := &net.IPAddr{IP: net.IPv4(224, 0, 0, 254)} // see RFC 4727 - type ml struct { - c *ipv4.PacketConn - ifi *net.Interface + c, err := net.ListenPacket("ip4:icmp", "0.0.0.0") // wildcard address + if err != nil { + t.Fatalf("net.ListenPacket failed: %v", err) } - var mlt []*ml + defer c.Close() + + r, err := ipv4.NewRawConn(c) + if err != nil { + t.Fatalf("ipv4.RawConn failed: %v", err) + } + gaddr := net.IPAddr{IP: net.IPv4(224, 0, 0, 254)} // see RFC 4727 + var mift []*net.Interface ift, err := net.Interfaces() if err != nil { t.Fatalf("net.Interfaces failed: %v", err) } for i, ifi := range ift { - ip, ok := isMulticastAvailable(&ifi) - if !ok { + if _, ok := isMulticastAvailable(&ifi); !ok { continue } - // listen to a unicast interface address - c, err := net.ListenPacket("udp4", ip.String()+":"+"1024") // see RFC 4727 - if err != nil { - t.Fatalf("net.ListenPacket with %v failed: %v", ip, err) - } - defer c.Close() - p := ipv4.NewPacketConn(c) - if err := p.JoinGroup(&ifi, gaddr); err != nil { - t.Fatalf("ipv4.PacketConn.JoinGroup on %v failed: %v", ifi, err) + if err := r.JoinGroup(&ifi, &gaddr); err != nil { + t.Fatalf("ipv4.RawConn.JoinGroup on %v failed: %v", ifi, err) } - mlt = append(mlt, &ml{p, &ift[i]}) + mift = append(mift, &ift[i]) } - for _, m := range mlt { - if err := m.c.LeaveGroup(m.ifi, gaddr); err != nil { - t.Fatalf("ipv4.PacketConn.LeaveGroup on %v failed: %v", m.ifi, err) + for _, ifi := range mift { + if err := r.LeaveGroup(ifi, &gaddr); err != nil { + t.Fatalf("ipv4.RawConn.LeaveGroup on %v failed: %v", ifi, err) } } } -func TestIPPerInterfaceSingleConnWithSingleGroupListener(t *testing.T) { +func TestIPPerInterfaceSingleRawConnWithSingleGroupListener(t *testing.T) { + switch runtime.GOOS { + case "plan9", "windows": + t.Skipf("not supported on %q", runtime.GOOS) + } if testing.Short() || !*testExternal { t.Skip("to avoid external network") } @@ -198,7 +210,7 @@ t.Skip("must be root") } - gaddr := &net.IPAddr{IP: net.IPv4(224, 0, 0, 254)} // see RFC 4727 + gaddr := net.IPAddr{IP: net.IPv4(224, 0, 0, 254)} // see RFC 4727 type ml struct { c *ipv4.RawConn ifi *net.Interface @@ -214,8 +226,7 @@ if !ok { continue } - // listen to a unicast interface address - c, err := net.ListenPacket("ip4:253", ip.String()) // see RFC 4727 + c, err := net.ListenPacket("ip4:253", ip.String()) // unicast address if err != nil { t.Fatalf("net.ListenPacket with %v failed: %v", ip, err) } @@ -224,13 +235,13 @@ if err != nil { t.Fatalf("ipv4.NewRawConn failed: %v", err) } - if err := r.JoinGroup(&ifi, gaddr); err != nil { + if err := r.JoinGroup(&ifi, &gaddr); err != nil { t.Fatalf("ipv4.RawConn.JoinGroup on %v failed: %v", ifi, err) } mlt = append(mlt, &ml{r, &ift[i]}) } for _, m := range mlt { - if err := m.c.LeaveGroup(m.ifi, gaddr); err != nil { + if err := m.c.LeaveGroup(m.ifi, &gaddr); err != nil { t.Fatalf("ipv4.RawConn.LeaveGroup on %v failed: %v", m.ifi, err) } } diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv4/packet.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv4/packet.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv4/packet.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv4/packet.go 2014-04-11 09:53:46.000000000 +0000 @@ -72,7 +72,7 @@ // Checksum = platform sets an appropriate value if Checksum is zero // Src = platform sets an appropriate value if Src is nil // Dst = -// h.Options = optional +// Options = optional func (c *packetHandler) WriteTo(h *Header, p []byte, cm *ControlMessage) error { if !c.ok() { return syscall.EINVAL diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv4/sockopt_plan9.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv4/sockopt_plan9.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv4/sockopt_plan9.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv4/sockopt_plan9.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ipv4 - -import "syscall" - -func ipv4HeaderPrepend(fd int) (bool, error) { - // TODO(mikio): Implement this - return false, syscall.EPLAN9 -} - -func setIPv4HeaderPrepend(fd int, v bool) error { - // TODO(mikio): Implement this - return syscall.EPLAN9 -} diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv4/sockopt_stub.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv4/sockopt_stub.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv4/sockopt_stub.go 1970-01-01 00:00:00.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv4/sockopt_stub.go 2014-04-11 09:53:46.000000000 +0000 @@ -0,0 +1,17 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build dragonfly plan9 solaris + +package ipv4 + +func ipv4HeaderPrepend(fd int) (bool, error) { + // TODO(mikio): Implement this + return false, errOpNoSupport +} + +func setIPv4HeaderPrepend(fd int, v bool) error { + // TODO(mikio): Implement this + return errOpNoSupport +} diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv4/sys_freebsd.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv4/sys_freebsd.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv4/sys_freebsd.go 1970-01-01 00:00:00.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv4/sys_freebsd.go 2014-04-11 09:53:46.000000000 +0000 @@ -0,0 +1,11 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv4 + +import "syscall" + +func init() { + freebsdVersion, _ = syscall.SysctlUint32("kern.osreldate") +} diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/control.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/control.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/control.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/control.go 2014-04-11 09:53:46.000000000 +0000 @@ -12,7 +12,6 @@ ) var ( - errNotSupported = errors.New("not supported") errMissingAddress = errors.New("missing address") errInvalidConnType = errors.New("invalid conn type") errNoSuchInterface = errors.New("no such interface") diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/control_rfc2292_darwin.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/control_rfc2292_darwin.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/control_rfc2292_darwin.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/control_rfc2292_darwin.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,151 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ipv6 - -import ( - "net" - "os" - "syscall" - "unsafe" -) - -const pktinfo = FlagDst | FlagInterface - -func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error { - opt.Lock() - defer opt.Unlock() - if cf&FlagHopLimit != 0 { - if err := setIPv6ReceiveHopLimit(fd, on); err != nil { - return err - } - if on { - opt.set(FlagHopLimit) - } else { - opt.clear(FlagHopLimit) - } - } - if cf&pktinfo != 0 { - if err := setIPv6ReceivePacketInfo(fd, on); err != nil { - return err - } - if on { - opt.set(cf & pktinfo) - } else { - opt.clear(cf & pktinfo) - } - } - return nil -} - -func newControlMessage(opt *rawOpt) (oob []byte) { - opt.Lock() - defer opt.Unlock() - l, off := 0, 0 - if opt.isset(FlagHopLimit) { - l += syscall.CmsgSpace(4) - } - if opt.isset(pktinfo) { - l += syscall.CmsgSpace(syscall.SizeofInet6Pktinfo) - } - if l > 0 { - oob = make([]byte, l) - if opt.isset(FlagHopLimit) { - m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off])) - m.Level = ianaProtocolIPv6 - m.Type = syscall.IPV6_2292HOPLIMIT - m.SetLen(syscall.CmsgLen(4)) - off += syscall.CmsgSpace(4) - } - if opt.isset(pktinfo) { - m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off])) - m.Level = ianaProtocolIPv6 - m.Type = syscall.IPV6_2292PKTINFO - m.SetLen(syscall.CmsgLen(syscall.SizeofInet6Pktinfo)) - off += syscall.CmsgSpace(syscall.SizeofInet6Pktinfo) - } - } - return -} - -func parseControlMessage(b []byte) (*ControlMessage, error) { - if len(b) == 0 { - return nil, nil - } - cmsgs, err := syscall.ParseSocketControlMessage(b) - if err != nil { - return nil, os.NewSyscallError("parse socket control message", err) - } - cm := &ControlMessage{} - for _, m := range cmsgs { - if m.Header.Level != ianaProtocolIPv6 { - continue - } - switch m.Header.Type { - case syscall.IPV6_2292HOPLIMIT: - cm.HopLimit = int(*(*byte)(unsafe.Pointer(&m.Data[:1][0]))) - case syscall.IPV6_2292PKTINFO: - pi := (*syscall.Inet6Pktinfo)(unsafe.Pointer(&m.Data[0])) - cm.IfIndex = int(pi.Ifindex) - cm.Dst = pi.Addr[:] - } - } - return cm, nil -} - -func marshalControlMessage(cm *ControlMessage) (oob []byte) { - if cm == nil { - return - } - l, off := 0, 0 - if cm.HopLimit > 0 { - l += syscall.CmsgSpace(4) - } - pion := false - if cm.Src.To4() == nil && cm.Src.To16() != nil || cm.IfIndex != 0 { - pion = true - l += syscall.CmsgSpace(syscall.SizeofInet6Pktinfo) - } - if len(cm.NextHop) == net.IPv6len { - l += syscall.CmsgSpace(syscall.SizeofSockaddrInet6) - } - if l > 0 { - oob = make([]byte, l) - if cm.HopLimit > 0 { - m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off])) - m.Level = ianaProtocolIPv6 - m.Type = syscall.IPV6_2292HOPLIMIT - m.SetLen(syscall.CmsgLen(4)) - data := oob[off+syscall.CmsgLen(0):] - *(*byte)(unsafe.Pointer(&data[:1][0])) = byte(cm.HopLimit) - off += syscall.CmsgSpace(4) - } - if pion { - m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off])) - m.Level = ianaProtocolIPv6 - m.Type = syscall.IPV6_2292PKTINFO - m.SetLen(syscall.CmsgLen(syscall.SizeofInet6Pktinfo)) - pi := (*syscall.Inet6Pktinfo)(unsafe.Pointer(&oob[off+syscall.CmsgLen(0)])) - if ip := cm.Src.To16(); ip != nil && ip.To4() == nil { - copy(pi.Addr[:], ip) - } - if cm.IfIndex != 0 { - pi.Ifindex = uint32(cm.IfIndex) - } - off += syscall.CmsgSpace(syscall.SizeofInet6Pktinfo) - } - if len(cm.NextHop) == net.IPv6len { - m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off])) - m.Level = ianaProtocolIPv6 - m.Type = syscall.IPV6_2292NEXTHOP - m.SetLen(syscall.CmsgLen(syscall.SizeofSockaddrInet6)) - sa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(&oob[off+syscall.CmsgLen(0)])) - sa.Len = syscall.SizeofSockaddrInet6 - sa.Family = syscall.AF_INET6 - copy(sa.Addr[:], cm.NextHop) - off += syscall.CmsgSpace(syscall.SizeofSockaddrInet6) - } - } - return -} diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/control_rfc2292_unix.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/control_rfc2292_unix.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/control_rfc2292_unix.go 1970-01-01 00:00:00.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/control_rfc2292_unix.go 2014-04-11 09:53:46.000000000 +0000 @@ -0,0 +1,151 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin + +package ipv6 + +import ( + "net" + "os" + "syscall" + "unsafe" +) + +const pktinfo = FlagDst | FlagInterface + +func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error { + opt.Lock() + defer opt.Unlock() + if cf&FlagHopLimit != 0 { + if err := setIPv6ReceiveHopLimit(fd, on); err != nil { + return err + } + if on { + opt.set(FlagHopLimit) + } else { + opt.clear(FlagHopLimit) + } + } + if cf&pktinfo != 0 { + if err := setIPv6ReceivePacketInfo(fd, on); err != nil { + return err + } + if on { + opt.set(cf & pktinfo) + } else { + opt.clear(cf & pktinfo) + } + } + return nil +} + +func newControlMessage(opt *rawOpt) (oob []byte) { + opt.Lock() + defer opt.Unlock() + l, off := 0, 0 + if opt.isset(FlagHopLimit) { + l += syscall.CmsgSpace(4) + } + if opt.isset(pktinfo) { + l += syscall.CmsgSpace(sysSizeofPacketInfo) + } + if l > 0 { + oob = make([]byte, l) + if opt.isset(FlagHopLimit) { + m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off])) + m.Level = ianaProtocolIPv6 + m.Type = sysSockopt2292HopLimit + m.SetLen(syscall.CmsgLen(4)) + off += syscall.CmsgSpace(4) + } + if opt.isset(pktinfo) { + m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off])) + m.Level = ianaProtocolIPv6 + m.Type = sysSockopt2292PacketInfo + m.SetLen(syscall.CmsgLen(sysSizeofPacketInfo)) + off += syscall.CmsgSpace(sysSizeofPacketInfo) + } + } + return +} + +func parseControlMessage(b []byte) (*ControlMessage, error) { + if len(b) == 0 { + return nil, nil + } + cmsgs, err := syscall.ParseSocketControlMessage(b) + if err != nil { + return nil, os.NewSyscallError("parse socket control message", err) + } + cm := &ControlMessage{} + for _, m := range cmsgs { + if m.Header.Level != ianaProtocolIPv6 { + continue + } + switch m.Header.Type { + case sysSockopt2292HopLimit: + cm.HopLimit = int(*(*byte)(unsafe.Pointer(&m.Data[:1][0]))) + case sysSockopt2292PacketInfo: + pi := (*sysPacketInfo)(unsafe.Pointer(&m.Data[0])) + cm.IfIndex = int(pi.IfIndex) + cm.Dst = pi.IP[:] + } + } + return cm, nil +} + +func marshalControlMessage(cm *ControlMessage) (oob []byte) { + if cm == nil { + return + } + l, off := 0, 0 + if cm.HopLimit > 0 { + l += syscall.CmsgSpace(4) + } + pion := false + if cm.Src.To4() == nil && cm.Src.To16() != nil || cm.IfIndex != 0 { + pion = true + l += syscall.CmsgSpace(sysSizeofPacketInfo) + } + if len(cm.NextHop) == net.IPv6len { + l += syscall.CmsgSpace(syscall.SizeofSockaddrInet6) + } + if l > 0 { + oob = make([]byte, l) + if cm.HopLimit > 0 { + m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off])) + m.Level = ianaProtocolIPv6 + m.Type = sysSockopt2292HopLimit + m.SetLen(syscall.CmsgLen(4)) + data := oob[off+syscall.CmsgLen(0):] + *(*byte)(unsafe.Pointer(&data[:1][0])) = byte(cm.HopLimit) + off += syscall.CmsgSpace(4) + } + if pion { + m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off])) + m.Level = ianaProtocolIPv6 + m.Type = sysSockopt2292PacketInfo + m.SetLen(syscall.CmsgLen(sysSizeofPacketInfo)) + pi := (*sysPacketInfo)(unsafe.Pointer(&oob[off+syscall.CmsgLen(0)])) + if ip := cm.Src.To16(); ip != nil && ip.To4() == nil { + copy(pi.IP[:], ip) + } + if cm.IfIndex != 0 { + pi.IfIndex = uint32(cm.IfIndex) + } + off += syscall.CmsgSpace(sysSizeofPacketInfo) + } + if len(cm.NextHop) == net.IPv6len { + m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off])) + m.Level = ianaProtocolIPv6 + m.Type = sysSockopt2292NextHop + m.SetLen(syscall.CmsgLen(syscall.SizeofSockaddrInet6)) + sa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(&oob[off+syscall.CmsgLen(0)])) + setSockaddr(sa, cm.NextHop, cm.IfIndex) + off += syscall.CmsgSpace(syscall.SizeofSockaddrInet6) + } + } + return +} diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/control_rfc3542_bsd.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/control_rfc3542_bsd.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/control_rfc3542_bsd.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/control_rfc3542_bsd.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,213 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build freebsd netbsd openbsd - -package ipv6 - -import ( - "net" - "os" - "syscall" - "unsafe" -) - -const pktinfo = FlagDst | FlagInterface - -func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error { - opt.Lock() - defer opt.Unlock() - if cf&FlagTrafficClass != 0 { - if err := setIPv6ReceiveTrafficClass(fd, on); err != nil { - return err - } - if on { - opt.set(FlagTrafficClass) - } else { - opt.clear(FlagTrafficClass) - } - } - if cf&FlagHopLimit != 0 { - if err := setIPv6ReceiveHopLimit(fd, on); err != nil { - return err - } - if on { - opt.set(FlagHopLimit) - } else { - opt.clear(FlagHopLimit) - } - } - if cf&pktinfo != 0 { - if err := setIPv6ReceivePacketInfo(fd, on); err != nil { - return err - } - if on { - opt.set(cf & pktinfo) - } else { - opt.clear(cf & pktinfo) - } - } - if cf&FlagPathMTU != 0 { - if err := setIPv6ReceivePathMTU(fd, on); err != nil { - return err - } - if on { - opt.set(FlagPathMTU) - } else { - opt.clear(FlagPathMTU) - } - } - return nil -} - -func newControlMessage(opt *rawOpt) (oob []byte) { - opt.Lock() - defer opt.Unlock() - l, off := 0, 0 - if opt.isset(FlagTrafficClass) { - l += syscall.CmsgSpace(4) - } - if opt.isset(FlagHopLimit) { - l += syscall.CmsgSpace(4) - } - if opt.isset(pktinfo) { - l += syscall.CmsgSpace(syscall.SizeofInet6Pktinfo) - } - if opt.isset(FlagPathMTU) { - l += syscall.CmsgSpace(syscall.SizeofIPv6MTUInfo) - } - if l > 0 { - oob = make([]byte, l) - if opt.isset(FlagTrafficClass) { - m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off])) - m.Level = ianaProtocolIPv6 - m.Type = syscall.IPV6_RECVTCLASS - m.SetLen(syscall.CmsgLen(4)) - off += syscall.CmsgSpace(4) - } - if opt.isset(FlagHopLimit) { - m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off])) - m.Level = ianaProtocolIPv6 - m.Type = syscall.IPV6_RECVHOPLIMIT - m.SetLen(syscall.CmsgLen(4)) - off += syscall.CmsgSpace(4) - } - if opt.isset(pktinfo) { - m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off])) - m.Level = ianaProtocolIPv6 - m.Type = syscall.IPV6_RECVPKTINFO - m.SetLen(syscall.CmsgLen(syscall.SizeofInet6Pktinfo)) - off += syscall.CmsgSpace(syscall.SizeofInet6Pktinfo) - } - if opt.isset(FlagPathMTU) { - m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off])) - m.Level = ianaProtocolIPv6 - m.Type = syscall.IPV6_RECVPATHMTU - m.SetLen(syscall.CmsgLen(syscall.SizeofIPv6MTUInfo)) - off += syscall.CmsgSpace(syscall.SizeofIPv6MTUInfo) - } - } - return -} - -func parseControlMessage(b []byte) (*ControlMessage, error) { - if len(b) == 0 { - return nil, nil - } - cmsgs, err := syscall.ParseSocketControlMessage(b) - if err != nil { - return nil, os.NewSyscallError("parse socket control message", err) - } - cm := &ControlMessage{} - for _, m := range cmsgs { - if m.Header.Level != ianaProtocolIPv6 { - continue - } - switch m.Header.Type { - case syscall.IPV6_TCLASS: - cm.TrafficClass = int(*(*byte)(unsafe.Pointer(&m.Data[:1][0]))) - case syscall.IPV6_HOPLIMIT: - cm.HopLimit = int(*(*byte)(unsafe.Pointer(&m.Data[:1][0]))) - case syscall.IPV6_PKTINFO: - pi := (*syscall.Inet6Pktinfo)(unsafe.Pointer(&m.Data[0])) - cm.Dst = pi.Addr[:] - cm.IfIndex = int(pi.Ifindex) - case syscall.IPV6_PATHMTU: - mi := (*syscall.IPv6MTUInfo)(unsafe.Pointer(&m.Data[0])) - cm.Dst = mi.Addr.Addr[:] - cm.IfIndex = int(mi.Addr.Scope_id) - cm.MTU = int(mi.Mtu) - } - } - return cm, nil -} - -func marshalControlMessage(cm *ControlMessage) (oob []byte) { - if cm == nil { - return - } - l, off := 0, 0 - if cm.TrafficClass > 0 { - l += syscall.CmsgSpace(4) - } - if cm.HopLimit > 0 { - l += syscall.CmsgSpace(4) - } - pion := false - if cm.Src.To4() == nil && cm.Src.To16() != nil || cm.IfIndex != 0 { - pion = true - l += syscall.CmsgSpace(syscall.SizeofInet6Pktinfo) - } - if len(cm.NextHop) == net.IPv6len { - l += syscall.CmsgSpace(syscall.SizeofSockaddrInet6) - } - if l > 0 { - oob = make([]byte, l) - if cm.TrafficClass > 0 { - m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off])) - m.Level = ianaProtocolIPv6 - m.Type = syscall.IPV6_TCLASS - m.SetLen(syscall.CmsgLen(4)) - data := oob[off+syscall.CmsgLen(0):] - *(*byte)(unsafe.Pointer(&data[:1][0])) = byte(cm.TrafficClass) - off += syscall.CmsgSpace(4) - } - if cm.HopLimit > 0 { - m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off])) - m.Level = ianaProtocolIPv6 - m.Type = syscall.IPV6_HOPLIMIT - m.SetLen(syscall.CmsgLen(4)) - data := oob[off+syscall.CmsgLen(0):] - *(*byte)(unsafe.Pointer(&data[:1][0])) = byte(cm.HopLimit) - off += syscall.CmsgSpace(4) - } - if pion { - m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off])) - m.Level = ianaProtocolIPv6 - m.Type = syscall.IPV6_PKTINFO - m.SetLen(syscall.CmsgLen(syscall.SizeofInet6Pktinfo)) - pi := (*syscall.Inet6Pktinfo)(unsafe.Pointer(&oob[off+syscall.CmsgLen(0)])) - if ip := cm.Src.To16(); ip != nil && ip.To4() == nil { - copy(pi.Addr[:], ip) - } - if cm.IfIndex != 0 { - pi.Ifindex = uint32(cm.IfIndex) - } - off += syscall.CmsgSpace(syscall.SizeofInet6Pktinfo) - } - if len(cm.NextHop) == net.IPv6len { - m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off])) - m.Level = ianaProtocolIPv6 - m.Type = syscall.IPV6_NEXTHOP - m.SetLen(syscall.CmsgLen(syscall.SizeofSockaddrInet6)) - sa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(&oob[off+syscall.CmsgLen(0)])) - sa.Len = syscall.SizeofSockaddrInet6 - sa.Family = syscall.AF_INET6 - copy(sa.Addr[:], cm.NextHop) - sa.Scope_id = uint32(cm.IfIndex) - off += syscall.CmsgSpace(syscall.SizeofSockaddrInet6) - } - } - return -} diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/control_rfc3542_linux.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/control_rfc3542_linux.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/control_rfc3542_linux.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/control_rfc3542_linux.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,217 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ipv6 - -import ( - "net" - "os" - "syscall" - "unsafe" -) - -const ( - // See /usr/include/linux/in6.h. - syscall_IPV6_RECVPATHMTU = syscall.IPV6_DSTOPTS + 1 + iota - syscall_IPV6_PATHMTU - syscall_IPV6_DONTFRAG -) - -const pktinfo = FlagDst | FlagInterface - -func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error { - opt.Lock() - defer opt.Unlock() - if cf&FlagTrafficClass != 0 { - if err := setIPv6ReceiveTrafficClass(fd, on); err != nil { - return err - } - if on { - opt.set(FlagTrafficClass) - } else { - opt.clear(FlagTrafficClass) - } - } - if cf&FlagHopLimit != 0 { - if err := setIPv6ReceiveHopLimit(fd, on); err != nil { - return err - } - if on { - opt.set(FlagHopLimit) - } else { - opt.clear(FlagHopLimit) - } - } - if cf&pktinfo != 0 { - if err := setIPv6ReceivePacketInfo(fd, on); err != nil { - return err - } - if on { - opt.set(cf & pktinfo) - } else { - opt.clear(cf & pktinfo) - } - } - if cf&FlagPathMTU != 0 { - if err := setIPv6ReceivePathMTU(fd, on); err != nil { - return err - } - if on { - opt.set(FlagPathMTU) - } else { - opt.clear(FlagPathMTU) - } - } - return nil -} - -func newControlMessage(opt *rawOpt) (oob []byte) { - opt.Lock() - defer opt.Unlock() - l, off := 0, 0 - if opt.isset(FlagTrafficClass) { - l += syscall.CmsgSpace(4) - } - if opt.isset(FlagHopLimit) { - l += syscall.CmsgSpace(4) - } - if opt.isset(pktinfo) { - l += syscall.CmsgSpace(syscall.SizeofInet6Pktinfo) - } - if opt.isset(FlagPathMTU) { - l += syscall.CmsgSpace(syscall.SizeofIPv6MTUInfo) - } - if l > 0 { - oob = make([]byte, l) - if opt.isset(FlagTrafficClass) { - m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off])) - m.Level = ianaProtocolIPv6 - m.Type = syscall.IPV6_RECVTCLASS - m.SetLen(syscall.CmsgLen(4)) - off += syscall.CmsgSpace(4) - } - if opt.isset(FlagHopLimit) { - m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off])) - m.Level = ianaProtocolIPv6 - m.Type = syscall.IPV6_RECVHOPLIMIT - m.SetLen(syscall.CmsgLen(4)) - off += syscall.CmsgSpace(4) - } - if opt.isset(pktinfo) { - m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off])) - m.Level = ianaProtocolIPv6 - m.Type = syscall.IPV6_RECVPKTINFO - m.SetLen(syscall.CmsgLen(syscall.SizeofInet6Pktinfo)) - off += syscall.CmsgSpace(syscall.SizeofInet6Pktinfo) - } - if opt.isset(FlagPathMTU) { - m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off])) - m.Level = ianaProtocolIPv6 - m.Type = syscall_IPV6_RECVPATHMTU - m.SetLen(syscall.CmsgLen(syscall.SizeofIPv6MTUInfo)) - off += syscall.CmsgSpace(syscall.SizeofIPv6MTUInfo) - } - } - return -} - -func parseControlMessage(b []byte) (*ControlMessage, error) { - if len(b) == 0 { - return nil, nil - } - cmsgs, err := syscall.ParseSocketControlMessage(b) - if err != nil { - return nil, os.NewSyscallError("parse socket control message", err) - } - cm := &ControlMessage{} - for _, m := range cmsgs { - if m.Header.Level != ianaProtocolIPv6 { - continue - } - switch m.Header.Type { - case syscall.IPV6_TCLASS: - cm.TrafficClass = int(*(*byte)(unsafe.Pointer(&m.Data[:1][0]))) - case syscall.IPV6_HOPLIMIT: - cm.HopLimit = int(*(*byte)(unsafe.Pointer(&m.Data[:1][0]))) - case syscall.IPV6_PKTINFO: - pi := (*syscall.Inet6Pktinfo)(unsafe.Pointer(&m.Data[0])) - cm.Dst = pi.Addr[:] - cm.IfIndex = int(pi.Ifindex) - case syscall_IPV6_PATHMTU: - mi := (*syscall.IPv6MTUInfo)(unsafe.Pointer(&m.Data[0])) - cm.Dst = mi.Addr.Addr[:] - cm.IfIndex = int(mi.Addr.Scope_id) - cm.MTU = int(mi.Mtu) - } - } - return cm, nil -} - -func marshalControlMessage(cm *ControlMessage) (oob []byte) { - if cm == nil { - return - } - l, off := 0, 0 - if cm.TrafficClass > 0 { - l += syscall.CmsgSpace(4) - } - if cm.HopLimit > 0 { - l += syscall.CmsgSpace(4) - } - pion := false - if cm.Src.To4() == nil && cm.Src.To16() != nil || cm.IfIndex != 0 { - pion = true - l += syscall.CmsgSpace(syscall.SizeofInet6Pktinfo) - } - if len(cm.NextHop) == net.IPv6len { - l += syscall.CmsgSpace(syscall.SizeofSockaddrInet6) - } - if l > 0 { - oob = make([]byte, l) - if cm.TrafficClass > 0 { - m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off])) - m.Level = ianaProtocolIPv6 - m.Type = syscall.IPV6_TCLASS - m.SetLen(syscall.CmsgLen(4)) - data := oob[off+syscall.CmsgLen(0):] - *(*byte)(unsafe.Pointer(&data[:1][0])) = byte(cm.TrafficClass) - off += syscall.CmsgSpace(4) - } - if cm.HopLimit > 0 { - m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off])) - m.Level = ianaProtocolIPv6 - m.Type = syscall.IPV6_HOPLIMIT - m.SetLen(syscall.CmsgLen(4)) - data := oob[off+syscall.CmsgLen(0):] - *(*byte)(unsafe.Pointer(&data[:1][0])) = byte(cm.HopLimit) - off += syscall.CmsgSpace(4) - } - if pion { - m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off])) - m.Level = ianaProtocolIPv6 - m.Type = syscall.IPV6_PKTINFO - m.SetLen(syscall.CmsgLen(syscall.SizeofInet6Pktinfo)) - pi := (*syscall.Inet6Pktinfo)(unsafe.Pointer(&oob[off+syscall.CmsgLen(0)])) - if ip := cm.Src.To16(); ip != nil && ip.To4() == nil { - copy(pi.Addr[:], ip) - } - if cm.IfIndex != 0 { - pi.Ifindex = uint32(cm.IfIndex) - } - off += syscall.CmsgSpace(syscall.SizeofInet6Pktinfo) - } - if len(cm.NextHop) == net.IPv6len { - m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off])) - m.Level = ianaProtocolIPv6 - m.Type = syscall.IPV6_NEXTHOP - m.SetLen(syscall.CmsgLen(syscall.SizeofSockaddrInet6)) - sa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(&oob[off+syscall.CmsgLen(0)])) - sa.Family = syscall.AF_INET6 - copy(sa.Addr[:], cm.NextHop) - sa.Scope_id = uint32(cm.IfIndex) - off += syscall.CmsgSpace(syscall.SizeofSockaddrInet6) - } - } - return -} diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/control_rfc3542_plan9.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/control_rfc3542_plan9.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/control_rfc3542_plan9.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/control_rfc3542_plan9.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,27 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ipv6 - -import "syscall" - -func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error { - // TODO(mikio): Implement this - return syscall.EPLAN9 -} - -func newControlMessage(opt *rawOpt) (oob []byte) { - // TODO(mikio): Implement this - return nil -} - -func parseControlMessage(b []byte) (*ControlMessage, error) { - // TODO(mikio): Implement this - return nil, syscall.EPLAN9 -} - -func marshalControlMessage(cm *ControlMessage) (oob []byte) { - // TODO(mikio): Implement this - return nil -} diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/control_rfc3542_stub.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/control_rfc3542_stub.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/control_rfc3542_stub.go 1970-01-01 00:00:00.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/control_rfc3542_stub.go 2014-04-11 09:53:46.000000000 +0000 @@ -0,0 +1,27 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build dragonfly plan9 solaris + +package ipv6 + +func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error { + // TODO(mikio): Implement this + return errOpNoSupport +} + +func newControlMessage(opt *rawOpt) (oob []byte) { + // TODO(mikio): Implement this + return nil +} + +func parseControlMessage(b []byte) (*ControlMessage, error) { + // TODO(mikio): Implement this + return nil, errOpNoSupport +} + +func marshalControlMessage(cm *ControlMessage) (oob []byte) { + // TODO(mikio): Implement this + return nil +} diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/control_rfc3542_unix.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/control_rfc3542_unix.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/control_rfc3542_unix.go 1970-01-01 00:00:00.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/control_rfc3542_unix.go 2014-04-11 09:53:46.000000000 +0000 @@ -0,0 +1,210 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build freebsd linux netbsd openbsd + +package ipv6 + +import ( + "net" + "os" + "syscall" + "unsafe" +) + +const pktinfo = FlagDst | FlagInterface + +func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error { + opt.Lock() + defer opt.Unlock() + if cf&FlagTrafficClass != 0 { + if err := setIPv6ReceiveTrafficClass(fd, on); err != nil { + return err + } + if on { + opt.set(FlagTrafficClass) + } else { + opt.clear(FlagTrafficClass) + } + } + if cf&FlagHopLimit != 0 { + if err := setIPv6ReceiveHopLimit(fd, on); err != nil { + return err + } + if on { + opt.set(FlagHopLimit) + } else { + opt.clear(FlagHopLimit) + } + } + if cf&pktinfo != 0 { + if err := setIPv6ReceivePacketInfo(fd, on); err != nil { + return err + } + if on { + opt.set(cf & pktinfo) + } else { + opt.clear(cf & pktinfo) + } + } + if cf&FlagPathMTU != 0 { + if err := setIPv6ReceivePathMTU(fd, on); err != nil { + return err + } + if on { + opt.set(FlagPathMTU) + } else { + opt.clear(FlagPathMTU) + } + } + return nil +} + +func newControlMessage(opt *rawOpt) (oob []byte) { + opt.Lock() + defer opt.Unlock() + l, off := 0, 0 + if opt.isset(FlagTrafficClass) { + l += syscall.CmsgSpace(4) + } + if opt.isset(FlagHopLimit) { + l += syscall.CmsgSpace(4) + } + if opt.isset(pktinfo) { + l += syscall.CmsgSpace(sysSizeofPacketInfo) + } + if opt.isset(FlagPathMTU) { + l += syscall.CmsgSpace(sysSizeofMTUInfo) + } + if l > 0 { + oob = make([]byte, l) + if opt.isset(FlagTrafficClass) { + m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off])) + m.Level = ianaProtocolIPv6 + m.Type = sysSockoptReceiveTrafficClass + m.SetLen(syscall.CmsgLen(4)) + off += syscall.CmsgSpace(4) + } + if opt.isset(FlagHopLimit) { + m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off])) + m.Level = ianaProtocolIPv6 + m.Type = sysSockoptReceiveHopLimit + m.SetLen(syscall.CmsgLen(4)) + off += syscall.CmsgSpace(4) + } + if opt.isset(pktinfo) { + m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off])) + m.Level = ianaProtocolIPv6 + m.Type = sysSockoptReceivePacketInfo + m.SetLen(syscall.CmsgLen(sysSizeofPacketInfo)) + off += syscall.CmsgSpace(sysSizeofPacketInfo) + } + if opt.isset(FlagPathMTU) { + m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off])) + m.Level = ianaProtocolIPv6 + m.Type = sysSockoptReceivePathMTU + m.SetLen(syscall.CmsgLen(sysSizeofMTUInfo)) + off += syscall.CmsgSpace(sysSizeofMTUInfo) + } + } + return +} + +func parseControlMessage(b []byte) (*ControlMessage, error) { + if len(b) == 0 { + return nil, nil + } + cmsgs, err := syscall.ParseSocketControlMessage(b) + if err != nil { + return nil, os.NewSyscallError("parse socket control message", err) + } + cm := &ControlMessage{} + for _, m := range cmsgs { + if m.Header.Level != ianaProtocolIPv6 { + continue + } + switch m.Header.Type { + case sysSockoptTrafficClass: + cm.TrafficClass = int(*(*byte)(unsafe.Pointer(&m.Data[:1][0]))) + case sysSockoptHopLimit: + cm.HopLimit = int(*(*byte)(unsafe.Pointer(&m.Data[:1][0]))) + case sysSockoptPacketInfo: + pi := (*sysPacketInfo)(unsafe.Pointer(&m.Data[0])) + cm.Dst = pi.IP[:] + cm.IfIndex = int(pi.IfIndex) + case sysSockoptPathMTU: + mi := (*sysMTUInfo)(unsafe.Pointer(&m.Data[0])) + cm.Dst = mi.Addr.Addr[:] + cm.IfIndex = int(mi.Addr.Scope_id) + cm.MTU = int(mi.MTU) + } + } + return cm, nil +} + +func marshalControlMessage(cm *ControlMessage) (oob []byte) { + if cm == nil { + return + } + l, off := 0, 0 + if cm.TrafficClass > 0 { + l += syscall.CmsgSpace(4) + } + if cm.HopLimit > 0 { + l += syscall.CmsgSpace(4) + } + pion := false + if cm.Src.To4() == nil && cm.Src.To16() != nil || cm.IfIndex != 0 { + pion = true + l += syscall.CmsgSpace(sysSizeofPacketInfo) + } + if len(cm.NextHop) == net.IPv6len { + l += syscall.CmsgSpace(syscall.SizeofSockaddrInet6) + } + if l > 0 { + oob = make([]byte, l) + if cm.TrafficClass > 0 { + m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off])) + m.Level = ianaProtocolIPv6 + m.Type = sysSockoptTrafficClass + m.SetLen(syscall.CmsgLen(4)) + data := oob[off+syscall.CmsgLen(0):] + *(*byte)(unsafe.Pointer(&data[:1][0])) = byte(cm.TrafficClass) + off += syscall.CmsgSpace(4) + } + if cm.HopLimit > 0 { + m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off])) + m.Level = ianaProtocolIPv6 + m.Type = sysSockoptHopLimit + m.SetLen(syscall.CmsgLen(4)) + data := oob[off+syscall.CmsgLen(0):] + *(*byte)(unsafe.Pointer(&data[:1][0])) = byte(cm.HopLimit) + off += syscall.CmsgSpace(4) + } + if pion { + m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off])) + m.Level = ianaProtocolIPv6 + m.Type = sysSockoptPacketInfo + m.SetLen(syscall.CmsgLen(sysSizeofPacketInfo)) + pi := (*sysPacketInfo)(unsafe.Pointer(&oob[off+syscall.CmsgLen(0)])) + if ip := cm.Src.To16(); ip != nil && ip.To4() == nil { + copy(pi.IP[:], ip) + } + if cm.IfIndex != 0 { + pi.IfIndex = uint32(cm.IfIndex) + } + off += syscall.CmsgSpace(sysSizeofPacketInfo) + } + if len(cm.NextHop) == net.IPv6len { + m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off])) + m.Level = ianaProtocolIPv6 + m.Type = sysSockoptNextHop + m.SetLen(syscall.CmsgLen(syscall.SizeofSockaddrInet6)) + sa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(&oob[off+syscall.CmsgLen(0)])) + setSockaddr(sa, cm.NextHop, cm.IfIndex) + off += syscall.CmsgSpace(syscall.SizeofSockaddrInet6) + } + } + return +} diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/control_test.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/control_test.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/control_test.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/control_test.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,42 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ipv6 - -import ( - "sync" - "testing" -) - -func TestControlFlags(t *testing.T) { - tf := FlagInterface | FlagPathMTU - opt := rawOpt{cflags: tf | FlagHopLimit} - - // This loop runs methods of raw.Opt concurrently for testing - // concurrent access to the rawOpt. The first entry shold be - // opt.set and the last entry should be opt.clear. - tfns := []func(ControlFlags){opt.set, opt.clear, opt.clear} - ch := make(chan bool) - var wg sync.WaitGroup - for i, fn := range tfns { - wg.Add(1) - go func(i int, fn func(ControlFlags)) { - defer wg.Done() - switch i { - case 0: - close(ch) - case len(tfns) - 1: - <-ch - } - opt.Lock() - defer opt.Unlock() - fn(tf) - }(i, fn) - } - wg.Wait() - - if opt.isset(tf) { - t.Fatalf("got %#x; expected %#x", opt.cflags, FlagHopLimit) - } -} diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/dgramopt_plan9.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/dgramopt_plan9.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/dgramopt_plan9.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/dgramopt_plan9.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,96 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ipv6 - -import ( - "net" - "syscall" -) - -// MulticastHopLimit returns the hop limit field value for outgoing -// multicast packets. -func (c *dgramOpt) MulticastHopLimit() (int, error) { - // TODO(mikio): Implement this - return 0, syscall.EPLAN9 -} - -// SetMulticastHopLimit sets the hop limit field value for future -// outgoing multicast packets. -func (c *dgramOpt) SetMulticastHopLimit(hoplim int) error { - // TODO(mikio): Implement this - return syscall.EPLAN9 -} - -// MulticastInterface returns the default interface for multicast -// packet transmissions. -func (c *dgramOpt) MulticastInterface() (*net.Interface, error) { - // TODO(mikio): Implement this - return nil, syscall.EPLAN9 -} - -// SetMulticastInterface sets the default interface for future -// multicast packet transmissions. -func (c *dgramOpt) SetMulticastInterface(ifi *net.Interface) error { - // TODO(mikio): Implement this - return syscall.EPLAN9 -} - -// MulticastLoopback reports whether transmitted multicast packets -// should be copied and send back to the originator. -func (c *dgramOpt) MulticastLoopback() (bool, error) { - // TODO(mikio): Implement this - return false, syscall.EPLAN9 -} - -// SetMulticastLoopback sets whether transmitted multicast packets -// should be copied and send back to the originator. -func (c *dgramOpt) SetMulticastLoopback(on bool) error { - // TODO(mikio): Implement this - return syscall.EPLAN9 -} - -// JoinGroup joins the group address group on the interface ifi. -// It uses the system assigned multicast interface when ifi is nil, -// although this is not recommended because the assignment depends on -// platforms and sometimes it might require routing configuration. -func (c *dgramOpt) JoinGroup(ifi *net.Interface, group net.Addr) error { - // TODO(mikio): Implement this - return syscall.EPLAN9 -} - -// LeaveGroup leaves the group address group on the interface ifi. -func (c *dgramOpt) LeaveGroup(ifi *net.Interface, group net.Addr) error { - // TODO(mikio): Implement this - return syscall.EPLAN9 -} - -// Checksum reports whether the kernel will compute, store or verify a -// checksum for both incoming and outgoing packets. If on is true, it -// returns an offset in bytes into the data of where the checksum -// field is located. -func (c *dgramOpt) Checksum() (on bool, offset int, err error) { - // TODO(mikio): Implement this - return false, 0, syscall.EPLAN9 -} - -// SetChecksum enables the kernel checksum processing. If on is ture, -// the offset should be an offset in bytes into the data of where the -// checksum field is located. -func (c *dgramOpt) SetChecksum(on bool, offset int) error { - // TODO(mikio): Implement this - return syscall.EPLAN9 -} - -// ICMPFilter returns an ICMP filter. -func (c *dgramOpt) ICMPFilter() (*ICMPFilter, error) { - // TODO(mikio): Implement this - return nil, syscall.EPLAN9 -} - -// SetICMPFilter deploys the ICMP filter. -func (c *dgramOpt) SetICMPFilter(f *ICMPFilter) error { - // TODO(mikio): Implement this - return syscall.EPLAN9 -} diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/dgramopt_stub.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/dgramopt_stub.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/dgramopt_stub.go 1970-01-01 00:00:00.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/dgramopt_stub.go 2014-04-11 09:53:46.000000000 +0000 @@ -0,0 +1,95 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build dragonfly plan9 solaris + +package ipv6 + +import "net" + +// MulticastHopLimit returns the hop limit field value for outgoing +// multicast packets. +func (c *dgramOpt) MulticastHopLimit() (int, error) { + // TODO(mikio): Implement this + return 0, errOpNoSupport +} + +// SetMulticastHopLimit sets the hop limit field value for future +// outgoing multicast packets. +func (c *dgramOpt) SetMulticastHopLimit(hoplim int) error { + // TODO(mikio): Implement this + return errOpNoSupport +} + +// MulticastInterface returns the default interface for multicast +// packet transmissions. +func (c *dgramOpt) MulticastInterface() (*net.Interface, error) { + // TODO(mikio): Implement this + return nil, errOpNoSupport +} + +// SetMulticastInterface sets the default interface for future +// multicast packet transmissions. +func (c *dgramOpt) SetMulticastInterface(ifi *net.Interface) error { + // TODO(mikio): Implement this + return errOpNoSupport +} + +// MulticastLoopback reports whether transmitted multicast packets +// should be copied and send back to the originator. +func (c *dgramOpt) MulticastLoopback() (bool, error) { + // TODO(mikio): Implement this + return false, errOpNoSupport +} + +// SetMulticastLoopback sets whether transmitted multicast packets +// should be copied and send back to the originator. +func (c *dgramOpt) SetMulticastLoopback(on bool) error { + // TODO(mikio): Implement this + return errOpNoSupport +} + +// JoinGroup joins the group address group on the interface ifi. +// It uses the system assigned multicast interface when ifi is nil, +// although this is not recommended because the assignment depends on +// platforms and sometimes it might require routing configuration. +func (c *dgramOpt) JoinGroup(ifi *net.Interface, group net.Addr) error { + // TODO(mikio): Implement this + return errOpNoSupport +} + +// LeaveGroup leaves the group address group on the interface ifi. +func (c *dgramOpt) LeaveGroup(ifi *net.Interface, group net.Addr) error { + // TODO(mikio): Implement this + return errOpNoSupport +} + +// Checksum reports whether the kernel will compute, store or verify a +// checksum for both incoming and outgoing packets. If on is true, it +// returns an offset in bytes into the data of where the checksum +// field is located. +func (c *dgramOpt) Checksum() (on bool, offset int, err error) { + // TODO(mikio): Implement this + return false, 0, errOpNoSupport +} + +// SetChecksum enables the kernel checksum processing. If on is ture, +// the offset should be an offset in bytes into the data of where the +// checksum field is located. +func (c *dgramOpt) SetChecksum(on bool, offset int) error { + // TODO(mikio): Implement this + return errOpNoSupport +} + +// ICMPFilter returns an ICMP filter. +func (c *dgramOpt) ICMPFilter() (*ICMPFilter, error) { + // TODO(mikio): Implement this + return nil, errOpNoSupport +} + +// SetICMPFilter deploys the ICMP filter. +func (c *dgramOpt) SetICMPFilter(f *ICMPFilter) error { + // TODO(mikio): Implement this + return errOpNoSupport +} diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/genericopt_plan9.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/genericopt_plan9.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/genericopt_plan9.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/genericopt_plan9.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ipv6 - -import "syscall" - -// TrafficClass returns the traffic class field value for outgoing -// packets. -func (c *genericOpt) TrafficClass() (int, error) { - // TODO(mikio): Implement this - return 0, syscall.EPLAN9 -} - -// SetTrafficClass sets the traffic class field value for future -// outgoing packets. -func (c *genericOpt) SetTrafficClass(tclass int) error { - // TODO(mikio): Implement this - return syscall.EPLAN9 -} - -// HopLimit returns the hop limit field value for outgoing packets. -func (c *genericOpt) HopLimit() (int, error) { - // TODO(mikio): Implement this - return 0, syscall.EPLAN9 -} - -// SetHopLimit sets the hop limit field value for future outgoing -// packets. -func (c *genericOpt) SetHopLimit(hoplim int) error { - // TODO(mikio): Implement this - return syscall.EPLAN9 -} diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/genericopt_stub.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/genericopt_stub.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/genericopt_stub.go 1970-01-01 00:00:00.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/genericopt_stub.go 2014-04-11 09:53:46.000000000 +0000 @@ -0,0 +1,34 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build dragonfly plan9 solaris + +package ipv6 + +// TrafficClass returns the traffic class field value for outgoing +// packets. +func (c *genericOpt) TrafficClass() (int, error) { + // TODO(mikio): Implement this + return 0, errOpNoSupport +} + +// SetTrafficClass sets the traffic class field value for future +// outgoing packets. +func (c *genericOpt) SetTrafficClass(tclass int) error { + // TODO(mikio): Implement this + return errOpNoSupport +} + +// HopLimit returns the hop limit field value for outgoing packets. +func (c *genericOpt) HopLimit() (int, error) { + // TODO(mikio): Implement this + return 0, errOpNoSupport +} + +// SetHopLimit sets the hop limit field value for future outgoing +// packets. +func (c *genericOpt) SetHopLimit(hoplim int) error { + // TODO(mikio): Implement this + return errOpNoSupport +} diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/gen.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/gen.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/gen.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/gen.go 2014-04-11 09:53:46.000000000 +0000 @@ -97,20 +97,16 @@ } type icmpv6Parameters struct { - XMLName xml.Name `xml:"registry"` - Title string `xml:"title"` - Updated string `xml:"updated"` - Registries []icmpv6ParamRegistry `xml:"registry"` -} - -type icmpv6ParamRegistry struct { - Title string `xml:"title"` - Records []icmpv6ParamRecord `xml:"record"` -} - -type icmpv6ParamRecord struct { - Value string `xml:"value"` - Name string `xml:"name"` + XMLName xml.Name `xml:"registry"` + Title string `xml:"title"` + Updated string `xml:"updated"` + Registries []struct { + Title string `xml:"title"` + Records []struct { + Value string `xml:"value"` + Name string `xml:"name"` + } `xml:"record"` + } `xml:"registry"` } type canonICMPv6ParamRecord struct { @@ -188,18 +184,16 @@ } type protocolNumbers struct { - XMLName xml.Name `xml:"registry"` - Title string `xml:"title"` - Updated string `xml:"updated"` - RegTitle string `xml:"registry>title"` - Note string `xml:"registry>note"` - Records []protocolRecord `xml:"registry>record"` -} - -type protocolRecord struct { - Value string `xml:"value"` - Name string `xml:"name"` - Descr string `xml:"description"` + XMLName xml.Name `xml:"registry"` + Title string `xml:"title"` + Updated string `xml:"updated"` + RegTitle string `xml:"registry>title"` + Note string `xml:"registry>note"` + Records []struct { + Value string `xml:"value"` + Name string `xml:"name"` + Descr string `xml:"description"` + } `xml:"registry>record"` } type canonProtocolRecord struct { diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/gentest.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/gentest.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/gentest.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/gentest.go 2014-04-11 09:53:46.000000000 +0000 @@ -39,7 +39,7 @@ func main() { var bb bytes.Buffer - fmt.Fprintf(&bb, "// go run gentv.go\n") + fmt.Fprintf(&bb, "// go run gentest.go\n") fmt.Fprintf(&bb, "// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT\n\n") fmt.Fprintf(&bb, "package ipv6_test\n\n") for _, r := range registries { @@ -85,18 +85,19 @@ } type dscpRegistry struct { - XMLName xml.Name `xml:"registry"` - Title string `xml:"title"` - Updated string `xml:"updated"` - Note string `xml:"note"` - RegTitle string `xml:"registry>title"` - PoolRecords []dscpRecord `xml:"registry>record"` - Records []dscpRecord `xml:"registry>registry>record"` -} - -type dscpRecord struct { - Name string `xml:"name"` - Space string `xml:"space"` + XMLName xml.Name `xml:"registry"` + Title string `xml:"title"` + Updated string `xml:"updated"` + Note string `xml:"note"` + RegTitle string `xml:"registry>title"` + PoolRecords []struct { + Name string `xml:"name"` + Space string `xml:"space"` + } `xml:"registry>record"` + Records []struct { + Name string `xml:"name"` + Space string `xml:"space"` + } `xml:"registry>registry>record"` } type canonDSCPRecord struct { @@ -145,17 +146,15 @@ } type tosTCByte struct { - XMLName xml.Name `xml:"registry"` - Title string `xml:"title"` - Updated string `xml:"updated"` - Note string `xml:"note"` - RegTitle string `xml:"registry>title"` - Records []tosTCByteRecord `xml:"registry>record"` -} - -type tosTCByteRecord struct { - Binary string `xml:"binary"` - Keyword string `xml:"keyword"` + XMLName xml.Name `xml:"registry"` + Title string `xml:"title"` + Updated string `xml:"updated"` + Note string `xml:"note"` + RegTitle string `xml:"registry>title"` + Records []struct { + Binary string `xml:"binary"` + Keyword string `xml:"keyword"` + } `xml:"registry>record"` } type canonTOSTCByteRecord struct { diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/helper.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/helper.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/helper.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/helper.go 2014-04-11 09:53:46.000000000 +0000 @@ -4,7 +4,12 @@ package ipv6 -import "net" +import ( + "errors" + "net" +) + +var errOpNoSupport = errors.New("operation not supported") func boolint(b bool) int { if b { diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/helper_plan9.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/helper_plan9.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/helper_plan9.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/helper_plan9.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,22 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ipv6 - -import "syscall" - -func (c *genericOpt) sysfd() (int, error) { - // TODO(mikio): Implement this - return 0, syscall.EPLAN9 -} - -func (c *dgramOpt) sysfd() (int, error) { - // TODO(mikio): Implement this - return 0, syscall.EPLAN9 -} - -func (c *payloadHandler) sysfd() (int, error) { - // TODO(mikio): Implement this - return 0, syscall.EPLAN9 -} diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/helper_stub.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/helper_stub.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/helper_stub.go 1970-01-01 00:00:00.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/helper_stub.go 2014-04-11 09:53:46.000000000 +0000 @@ -0,0 +1,22 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build dragonfly plan9 solaris + +package ipv6 + +func (c *genericOpt) sysfd() (int, error) { + // TODO(mikio): Implement this + return 0, errOpNoSupport +} + +func (c *dgramOpt) sysfd() (int, error) { + // TODO(mikio): Implement this + return 0, errOpNoSupport +} + +func (c *payloadHandler) sysfd() (int, error) { + // TODO(mikio): Implement this + return 0, errOpNoSupport +} diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/iana.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/iana.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/iana.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/iana.go 2014-04-11 09:53:46.000000000 +0000 @@ -3,7 +3,7 @@ package ipv6 -// Internet Control Message Protocol version 6 (ICMPv6) Parameters, Updated: 2012-11-12 +// Internet Control Message Protocol version 6 (ICMPv6) Parameters, Updated: 2013-07-03 const ( ICMPTypeDestinationUnreachable ICMPType = 1 // Destination Unreachable ICMPTypePacketTooBig ICMPType = 2 // Packet Too Big @@ -41,7 +41,7 @@ ICMPTypeDuplicateAddressConfirmation ICMPType = 158 // Duplicate Address Confirmation ) -// Internet Control Message Protocol version 6 (ICMPv6) Parameters, Updated: 2012-11-12 +// Internet Control Message Protocol version 6 (ICMPv6) Parameters, Updated: 2013-07-03 var icmpTypes = map[ICMPType]string{ 1: "destination unreachable", 2: "packet too big", diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/iana_test.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/iana_test.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/iana_test.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/iana_test.go 2014-04-11 09:53:46.000000000 +0000 @@ -1,9 +1,9 @@ -// go run gentv.go +// go run gentest.go // GENERATED BY THE COMMAND ABOVE; DO NOT EDIT package ipv6_test -// Differentiated Services Field Codepoints, Updated: 2010-05-11 +// Differentiated Services Field Codepoints (DSCP), Updated: 2013-06-25 const ( DiffServCS0 = 0x0 // CS0 DiffServCS1 = 0x20 // CS1 diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/icmp_bsd.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/icmp_bsd.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/icmp_bsd.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/icmp_bsd.go 2014-04-11 09:53:46.000000000 +0000 @@ -6,13 +6,11 @@ package ipv6 -import "syscall" - -type rawICMPFilter struct { - syscall.ICMPv6Filter +type sysICMPFilter struct { + Filt [8]uint32 } -func (f *rawICMPFilter) set(typ ICMPType, block bool) { +func (f *sysICMPFilter) set(typ ICMPType, block bool) { if block { f.Filt[typ>>5] &^= 1 << (uint32(typ) & 31) } else { @@ -20,7 +18,7 @@ } } -func (f *rawICMPFilter) setAll(block bool) { +func (f *sysICMPFilter) setAll(block bool) { for i := range f.Filt { if block { f.Filt[i] = 0 @@ -30,6 +28,6 @@ } } -func (f *rawICMPFilter) willBlock(typ ICMPType) bool { +func (f *sysICMPFilter) willBlock(typ ICMPType) bool { return f.Filt[typ>>5]&(1<<(uint32(typ)&31)) == 0 } diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/icmp.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/icmp.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/icmp.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/icmp.go 2014-04-11 09:53:46.000000000 +0000 @@ -21,26 +21,27 @@ // packets. type ICMPFilter struct { mu sync.RWMutex - rawICMPFilter + sysICMPFilter } // Set sets the ICMP type and filter action to the filter. func (f *ICMPFilter) Set(typ ICMPType, block bool) { f.mu.Lock() - defer f.mu.Unlock() f.set(typ, block) + f.mu.Unlock() } // SetAll sets the filter action to the filter. func (f *ICMPFilter) SetAll(block bool) { f.mu.Lock() - defer f.mu.Unlock() f.setAll(block) + f.mu.Unlock() } // WillBlock reports whether the ICMP type will be blocked. func (f *ICMPFilter) WillBlock(typ ICMPType) bool { f.mu.RLock() - defer f.mu.RUnlock() - return f.willBlock(typ) + ok := f.willBlock(typ) + f.mu.RUnlock() + return ok } diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/icmp_linux.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/icmp_linux.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/icmp_linux.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/icmp_linux.go 2014-04-11 09:53:46.000000000 +0000 @@ -4,13 +4,11 @@ package ipv6 -import "syscall" - -type rawICMPFilter struct { - syscall.ICMPv6Filter +type sysICMPFilter struct { + Data [8]uint32 } -func (f *rawICMPFilter) set(typ ICMPType, block bool) { +func (f *sysICMPFilter) set(typ ICMPType, block bool) { if block { f.Data[typ>>5] |= 1 << (uint32(typ) & 31) } else { @@ -18,7 +16,7 @@ } } -func (f *rawICMPFilter) setAll(block bool) { +func (f *sysICMPFilter) setAll(block bool) { for i := range f.Data { if block { f.Data[i] = 1<<32 - 1 @@ -28,6 +26,6 @@ } } -func (f *rawICMPFilter) willBlock(typ ICMPType) bool { +func (f *sysICMPFilter) willBlock(typ ICMPType) bool { return f.Data[typ>>5]&(1<<(uint32(typ)&31)) != 0 } diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/icmp_plan9.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/icmp_plan9.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/icmp_plan9.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/icmp_plan9.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,22 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ipv6 - -type rawICMPFilter struct { - // TODO(mikio): Implement this -} - -func (f *rawICMPFilter) set(typ ICMPType, block bool) { - // TODO(mikio): Implement this -} - -func (f *rawICMPFilter) setAll(block bool) { - // TODO(mikio): Implement this -} - -func (f *rawICMPFilter) willBlock(typ ICMPType) bool { - // TODO(mikio): Implement this - return false -} diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/icmp_stub.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/icmp_stub.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/icmp_stub.go 1970-01-01 00:00:00.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/icmp_stub.go 2014-04-11 09:53:46.000000000 +0000 @@ -0,0 +1,24 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build dragonfly plan9 solaris + +package ipv6 + +type sysICMPFilter struct { + // TODO(mikio): Implement this +} + +func (f *sysICMPFilter) set(typ ICMPType, block bool) { + // TODO(mikio): Implement this +} + +func (f *sysICMPFilter) setAll(block bool) { + // TODO(mikio): Implement this +} + +func (f *sysICMPFilter) willBlock(typ ICMPType) bool { + // TODO(mikio): Implement this + return false +} diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/icmp_test.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/icmp_test.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/icmp_test.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/icmp_test.go 2014-04-11 09:53:46.000000000 +0000 @@ -14,9 +14,27 @@ "testing" ) +var icmpStringTests = []struct { + in ipv6.ICMPType + out string +}{ + {ipv6.ICMPTypeDestinationUnreachable, "destination unreachable"}, + + {256, ""}, +} + +func TestICMPString(t *testing.T) { + for _, tt := range icmpStringTests { + s := tt.in.String() + if s != tt.out { + t.Errorf("got %s; expected %s", s, tt.out) + } + } +} + func TestICMPFilter(t *testing.T) { switch runtime.GOOS { - case "plan9", "windows": + case "dragonfly", "plan9", "solaris", "windows": t.Skipf("not supported on %q", runtime.GOOS) } @@ -49,7 +67,7 @@ func TestSetICMPFilter(t *testing.T) { switch runtime.GOOS { - case "plan9", "windows": + case "dragonfly", "plan9", "solaris", "windows": t.Skipf("not supported on %q", runtime.GOOS) } if !supportsIPv6 { diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/icmp_windows.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/icmp_windows.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/icmp_windows.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/icmp_windows.go 2014-04-11 09:53:46.000000000 +0000 @@ -4,19 +4,19 @@ package ipv6 -type rawICMPFilter struct { +type sysICMPFilter struct { // TODO(mikio): Implement this } -func (f *rawICMPFilter) set(typ ICMPType, block bool) { +func (f *sysICMPFilter) set(typ ICMPType, block bool) { // TODO(mikio): Implement this } -func (f *rawICMPFilter) setAll(block bool) { +func (f *sysICMPFilter) setAll(block bool) { // TODO(mikio): Implement this } -func (f *rawICMPFilter) willBlock(typ ICMPType) bool { +func (f *sysICMPFilter) willBlock(typ ICMPType) bool { // TODO(mikio): Implement this return false } diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/mockicmp_test.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/mockicmp_test.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/mockicmp_test.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/mockicmp_test.go 2014-04-11 09:53:46.000000000 +0000 @@ -7,8 +7,22 @@ import ( "code.google.com/p/go.net/ipv6" "errors" + "net" ) +const ( + ipv6PseudoHeaderLen = 2*net.IPv6len + 8 + ianaProtocolIPv6ICMP = 58 +) + +func ipv6PseudoHeader(src, dst net.IP, nextHeader int) []byte { + b := make([]byte, ipv6PseudoHeaderLen) + copy(b[:net.IPv6len], src) + copy(b[net.IPv6len:], dst) + b[len(b)-1] = byte(nextHeader) + return b +} + // icmpMessage represents an ICMP message. type icmpMessage struct { Type ipv6.ICMPType // type @@ -25,8 +39,11 @@ // Marshal returns the binary enconding of the ICMP echo request or // reply message m. -func (m *icmpMessage) Marshal() ([]byte, error) { +func (m *icmpMessage) Marshal(psh []byte) ([]byte, error) { b := []byte{byte(m.Type), byte(m.Code), 0, 0} + if psh != nil { + b = append(psh, b...) + } if m.Body != nil && m.Body.Len() != 0 { mb, err := m.Body.Marshal() if err != nil { @@ -34,10 +51,11 @@ } b = append(b, mb...) } - switch m.Type { - case ipv6.ICMPTypeEchoRequest, ipv6.ICMPTypeEchoReply: + if psh == nil { return b, nil } + off, l := 2*net.IPv6len, len(b)-len(psh) + b[off], b[off+1], b[off+2], b[off+3] = byte(l>>24), byte(l>>16), byte(l>>8), byte(l) csumcv := len(b) - 1 // checksum coverage s := uint32(0) for i := 0; i < csumcv; i += 2 { @@ -50,9 +68,9 @@ s = s + s>>16 // Place checksum back in header; using ^= avoids the // assumption the checksum bytes are zero. - b[2] ^= byte(^s) - b[3] ^= byte(^s >> 8) - return b, nil + b[len(psh)+2] ^= byte(^s) + b[len(psh)+3] ^= byte(^s >> 8) + return b[len(psh):], nil } // parseICMPMessage parses b as an ICMP message. diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/mocktransponder_test.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/mocktransponder_test.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/mocktransponder_test.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/mocktransponder_test.go 2014-04-11 09:53:46.000000000 +0000 @@ -86,25 +86,3 @@ } c.Close() } - -func transponder(t *testing.T, ln net.Listener, done chan<- bool) { - defer func() { done <- true }() - - c, err := ln.Accept() - if err != nil { - t.Errorf("net.Listener.Accept failed: %v", err) - return - } - defer c.Close() - - b := make([]byte, 128) - n, err := c.Read(b) - if err != nil { - t.Errorf("net.Conn.Read failed: %v", err) - return - } - if _, err := c.Write(b[:n]); err != nil { - t.Errorf("net.Conn.Write failed: %v", err) - return - } -} diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/multicastlistener_test.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/multicastlistener_test.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/multicastlistener_test.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/multicastlistener_test.go 2014-04-11 09:53:46.000000000 +0000 @@ -21,7 +21,7 @@ func TestUDPSinglePacketConnWithMultipleGroupListeners(t *testing.T) { switch runtime.GOOS { - case "plan9", "windows": + case "dragonfly", "plan9", "solaris", "windows": t.Skipf("not supported on %q", runtime.GOOS) } if !supportsIPv6 { @@ -59,9 +59,9 @@ } } -func TestUDPMultipleConnWithMultipleGroupListeners(t *testing.T) { +func TestUDPMultiplePacketConnWithMultipleGroupListeners(t *testing.T) { switch runtime.GOOS { - case "plan9", "windows": + case "dragonfly", "plan9", "solaris", "windows": t.Skipf("not supported on %q", runtime.GOOS) } if !supportsIPv6 { @@ -113,14 +113,14 @@ func TestUDPPerInterfaceSinglePacketConnWithSingleGroupListener(t *testing.T) { switch runtime.GOOS { - case "plan9", "windows": + case "dragonfly", "plan9", "solaris", "windows": t.Skipf("not supported on %q", runtime.GOOS) } if !supportsIPv6 { t.Skip("ipv6 is not supported") } - gaddr := &net.IPAddr{IP: net.ParseIP("ff02::114")} // see RFC 4727 + gaddr := net.IPAddr{IP: net.ParseIP("ff02::114")} // see RFC 4727 type ml struct { c *ipv6.PacketConn ifi *net.Interface @@ -142,13 +142,13 @@ } defer c.Close() p := ipv6.NewPacketConn(c) - if err := p.JoinGroup(&ifi, gaddr); err != nil { + if err := p.JoinGroup(&ifi, &gaddr); err != nil { t.Fatalf("ipv6.PacketConn.JoinGroup on %v failed: %v", ifi, err) } mlt = append(mlt, &ml{p, &ift[i]}) } for _, m := range mlt { - if err := m.c.LeaveGroup(m.ifi, gaddr); err != nil { + if err := m.c.LeaveGroup(m.ifi, &gaddr); err != nil { t.Fatalf("ipv6.PacketConn.LeaveGroup on %v failed: %v", m.ifi, err) } } @@ -156,7 +156,7 @@ func TestIPSinglePacketConnWithSingleGroupListener(t *testing.T) { switch runtime.GOOS { - case "plan9", "windows": + case "dragonfly", "plan9", "solaris", "windows": t.Skipf("not supported on %q", runtime.GOOS) } if !supportsIPv6 { @@ -166,14 +166,14 @@ t.Skip("must be root") } - c, err := net.ListenPacket("ip6:ipv6-icmp", "::") + c, err := net.ListenPacket("ip6:ipv6-icmp", "::") // wildcard address if err != nil { t.Fatalf("net.ListenPacket failed: %v", err) } defer c.Close() p := ipv6.NewPacketConn(c) - gaddr := &net.IPAddr{IP: net.ParseIP("ff02::114")} // see RFC 4727 + gaddr := net.IPAddr{IP: net.ParseIP("ff02::114")} // see RFC 4727 var mift []*net.Interface ift, err := net.Interfaces() @@ -184,14 +184,60 @@ if _, ok := isMulticastAvailable(&ifi); !ok { continue } - if err := p.JoinGroup(&ifi, gaddr); err != nil { + if err := p.JoinGroup(&ifi, &gaddr); err != nil { t.Fatalf("ipv6.PacketConn.JoinGroup on %v failed: %v", ifi, err) } mift = append(mift, &ift[i]) } for _, ifi := range mift { - if err := p.LeaveGroup(ifi, gaddr); err != nil { + if err := p.LeaveGroup(ifi, &gaddr); err != nil { t.Fatalf("ipv6.PacketConn.LeaveGroup on %v failed: %v", ifi, err) } } } + +func TestIPPerInterfaceSinglePacketConnWithSingleGroupListener(t *testing.T) { + switch runtime.GOOS { + case "darwin", "dragonfly", "plan9", "solaris", "windows": + t.Skipf("not supported on %q", runtime.GOOS) + } + if !supportsIPv6 { + t.Skip("ipv6 is not supported") + } + if os.Getuid() != 0 { + t.Skip("must be root") + } + + gaddr := net.IPAddr{IP: net.ParseIP("ff02::114")} // see RFC 4727 + type ml struct { + c *ipv6.PacketConn + ifi *net.Interface + } + var mlt []*ml + + ift, err := net.Interfaces() + if err != nil { + t.Fatalf("net.Interfaces failed: %v", err) + } + for i, ifi := range ift { + ip, ok := isMulticastAvailable(&ifi) + if !ok { + continue + } + c, err := net.ListenPacket("ip6:ipv6-icmp", fmt.Sprintf("%s%%%s", ip.String(), ifi.Name)) // unicast address + if err != nil { + t.Fatalf("net.ListenPacket failed: %v", err) + } + defer c.Close() + p := ipv6.NewPacketConn(c) + if err := p.JoinGroup(&ifi, &gaddr); err != nil { + t.Fatalf("ipv6.PacketConn.JoinGroup on %v failed: %v", ifi, err) + } + mlt = append(mlt, &ml{p, &ift[i]}) + } + for _, m := range mlt { + if err := m.c.LeaveGroup(m.ifi, &gaddr); err != nil { + t.Fatalf("ipv6.PacketConn.LeaveGroup on %v failed: %v", m.ifi, err) + } + } +} diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/multicastsockopt_test.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/multicastsockopt_test.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/multicastsockopt_test.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/multicastsockopt_test.go 2014-04-11 09:53:46.000000000 +0000 @@ -22,7 +22,7 @@ func TestPacketConnMulticastSocketOptions(t *testing.T) { switch runtime.GOOS { - case "plan9", "windows": + case "dragonfly", "plan9", "solaris", "windows": t.Skipf("not supported on %q", runtime.GOOS) } if !supportsIPv6 { diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/multicast_test.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/multicast_test.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/multicast_test.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/multicast_test.go 2014-04-11 09:53:46.000000000 +0000 @@ -5,11 +5,13 @@ package ipv6_test import ( + "bytes" "code.google.com/p/go.net/ipv6" "net" "os" "runtime" "testing" + "time" ) func TestPacketConnReadWriteMulticastUDP(t *testing.T) { @@ -17,7 +19,7 @@ case "freebsd": // due to a bug on loopback marking // See http://www.freebsd.org/cgi/query-pr.cgi?pr=180065. t.Skipf("not supported on %q", runtime.GOOS) - case "plan9", "windows": + case "dragonfly", "plan9", "solaris", "windows": t.Skipf("not supported on %q", runtime.GOOS) } if !supportsIPv6 { @@ -44,33 +46,49 @@ } p := ipv6.NewPacketConn(c) + defer p.Close() if err := p.JoinGroup(ifi, dst); err != nil { t.Fatalf("ipv6.PacketConn.JoinGroup on %v failed: %v", ifi, err) } if err := p.SetMulticastInterface(ifi); err != nil { t.Fatalf("ipv6.PacketConn.SetMulticastInterface failed: %v", err) } + if _, err := p.MulticastInterface(); err != nil { + t.Fatalf("ipv6.PacketConn.MulticastInterface failed: %v", err) + } if err := p.SetMulticastLoopback(true); err != nil { t.Fatalf("ipv6.PacketConn.SetMulticastLoopback failed: %v", err) } + if _, err := p.MulticastLoopback(); err != nil { + t.Fatalf("ipv6.PacketConn.MulticastLoopback failed: %v", err) + } cm := ipv6.ControlMessage{ TrafficClass: DiffServAF11 | CongestionExperienced, + Src: net.IPv6loopback, IfIndex: ifi.Index, } - cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagInterface | ipv6.FlagPathMTU + cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagSrc | ipv6.FlagDst | ipv6.FlagInterface | ipv6.FlagPathMTU + wb := []byte("HELLO-R-U-THERE") for i, toggle := range []bool{true, false, true} { if err := p.SetControlMessage(cf, toggle); err != nil { t.Fatalf("ipv6.PacketConn.SetControlMessage failed: %v", err) } + if err := p.SetDeadline(time.Now().Add(200 * time.Millisecond)); err != nil { + t.Fatalf("ipv6.PacketConn.SetDeadline failed: %v", err) + } cm.HopLimit = i + 1 - if _, err := p.WriteTo([]byte("HELLO-R-U-THERE"), &cm, dst); err != nil { + if n, err := p.WriteTo(wb, &cm, dst); err != nil { t.Fatalf("ipv6.PacketConn.WriteTo failed: %v", err) + } else if n != len(wb) { + t.Fatalf("ipv6.PacketConn.WriteTo failed: short write: %v", n) } - b := make([]byte, 128) - if _, cm, _, err := p.ReadFrom(b); err != nil { + rb := make([]byte, 128) + if n, cm, _, err := p.ReadFrom(rb); err != nil { t.Fatalf("ipv6.PacketConn.ReadFrom failed: %v", err) + } else if !bytes.Equal(rb[:n], wb) { + t.Fatalf("got %v; expected %v", rb[:n], wb) } else { t.Logf("rcvd cmsg: %v", cm) } @@ -79,7 +97,7 @@ func TestPacketConnReadWriteMulticastICMP(t *testing.T) { switch runtime.GOOS { - case "plan9", "windows": + case "dragonfly", "plan9", "solaris", "windows": t.Skipf("not supported on %q", runtime.GOOS) } if !supportsIPv6 { @@ -104,22 +122,31 @@ t.Fatalf("net.ResolveIPAddr failed: %v", err) } + pshicmp := ipv6PseudoHeader(c.LocalAddr().(*net.IPAddr).IP, dst.IP, ianaProtocolIPv6ICMP) p := ipv6.NewPacketConn(c) + defer p.Close() if err := p.JoinGroup(ifi, dst); err != nil { t.Fatalf("ipv6.PacketConn.JoinGroup on %v failed: %v", ifi, err) } if err := p.SetMulticastInterface(ifi); err != nil { t.Fatalf("ipv6.PacketConn.SetMulticastInterface failed: %v", err) } + if _, err := p.MulticastInterface(); err != nil { + t.Fatalf("ipv6.PacketConn.MulticastInterface failed: %v", err) + } if err := p.SetMulticastLoopback(true); err != nil { t.Fatalf("ipv6.PacketConn.SetMulticastLoopback failed: %v", err) } + if _, err := p.MulticastLoopback(); err != nil { + t.Fatalf("ipv6.PacketConn.MulticastLoopback failed: %v", err) + } cm := ipv6.ControlMessage{ TrafficClass: DiffServAF11 | CongestionExperienced, + Src: net.IPv6loopback, IfIndex: ifi.Index, } - cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagInterface | ipv6.FlagPathMTU + cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagSrc | ipv6.FlagDst | ipv6.FlagInterface | ipv6.FlagPathMTU var f ipv6.ICMPFilter f.SetAll(true) @@ -128,30 +155,47 @@ t.Fatalf("ipv6.PacketConn.SetICMPFilter failed: %v", err) } + var psh []byte for i, toggle := range []bool{true, false, true} { + if toggle { + psh = nil + if err := p.SetChecksum(true, 2); err != nil { + t.Fatalf("ipv6.PacketConn.SetChecksum failed: %v", err) + } + } else { + psh = pshicmp + // Some platforms never allow to disable the + // kernel checksum processing. + p.SetChecksum(false, -1) + } wb, err := (&icmpMessage{ Type: ipv6.ICMPTypeEchoRequest, Code: 0, Body: &icmpEcho{ ID: os.Getpid() & 0xffff, Seq: i + 1, Data: []byte("HELLO-R-U-THERE"), }, - }).Marshal() + }).Marshal(psh) if err != nil { t.Fatalf("icmpMessage.Marshal failed: %v", err) } if err := p.SetControlMessage(cf, toggle); err != nil { t.Fatalf("ipv6.PacketConn.SetControlMessage failed: %v", err) } + if err := p.SetDeadline(time.Now().Add(200 * time.Millisecond)); err != nil { + t.Fatalf("ipv6.PacketConn.SetDeadline failed: %v", err) + } cm.HopLimit = i + 1 - if _, err := p.WriteTo(wb, &cm, dst); err != nil { + if n, err := p.WriteTo(wb, &cm, dst); err != nil { t.Fatalf("ipv6.PacketConn.WriteTo failed: %v", err) + } else if n != len(wb) { + t.Fatalf("ipv6.PacketConn.WriteTo failed: short write: %v", n) } - b := make([]byte, 128) - if n, cm, _, err := p.ReadFrom(b); err != nil { + rb := make([]byte, 128) + if n, cm, _, err := p.ReadFrom(rb); err != nil { t.Fatalf("ipv6.PacketConn.ReadFrom failed: %v", err) } else { t.Logf("rcvd cmsg: %v", cm) - if m, err := parseICMPMessage(b[:n]); err != nil { + if m, err := parseICMPMessage(rb[:n]); err != nil { t.Fatalf("parseICMPMessage failed: %v", err) } else if m.Type != ipv6.ICMPTypeEchoReply || m.Code != 0 { t.Fatalf("got type=%v, code=%v; expected type=%v, code=%v", m.Type, m.Code, ipv6.ICMPTypeEchoReply, 0) diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/readwrite_test.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/readwrite_test.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/readwrite_test.go 1970-01-01 00:00:00.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/readwrite_test.go 2014-04-11 09:53:46.000000000 +0000 @@ -0,0 +1,168 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv6_test + +import ( + "bytes" + "code.google.com/p/go.net/ipv6" + "net" + "runtime" + "sync" + "testing" +) + +func benchmarkUDPListener() (net.PacketConn, net.Addr, error) { + c, err := net.ListenPacket("udp6", "[::1]:0") + if err != nil { + return nil, nil, err + } + dst, err := net.ResolveUDPAddr("udp6", c.LocalAddr().String()) + if err != nil { + c.Close() + return nil, nil, err + } + return c, dst, nil +} + +func BenchmarkReadWriteNetUDP(b *testing.B) { + c, dst, err := benchmarkUDPListener() + if err != nil { + b.Fatalf("benchmarkUDPListener failed: %v", err) + } + defer c.Close() + + wb, rb := []byte("HELLO-R-U-THERE"), make([]byte, 128) + b.ResetTimer() + for i := 0; i < b.N; i++ { + benchmarkReadWriteNetUDP(b, c, wb, rb, dst) + } +} + +func benchmarkReadWriteNetUDP(b *testing.B, c net.PacketConn, wb, rb []byte, dst net.Addr) { + if _, err := c.WriteTo(wb, dst); err != nil { + b.Fatalf("net.PacketConn.WriteTo failed: %v", err) + } + if _, _, err := c.ReadFrom(rb); err != nil { + b.Fatalf("net.PacketConn.ReadFrom failed: %v", err) + } +} + +func BenchmarkReadWriteIPv6UDP(b *testing.B) { + c, dst, err := benchmarkUDPListener() + if err != nil { + b.Fatalf("benchmarkUDPListener failed: %v", err) + } + defer c.Close() + + p := ipv6.NewPacketConn(c) + cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagInterface | ipv6.FlagPathMTU + if err := p.SetControlMessage(cf, true); err != nil { + b.Fatalf("ipv6.PacketConn.SetControlMessage failed: %v", err) + } + ifi := loopbackInterface() + + wb, rb := []byte("HELLO-R-U-THERE"), make([]byte, 128) + b.ResetTimer() + for i := 0; i < b.N; i++ { + benchmarkReadWriteIPv6UDP(b, p, wb, rb, dst, ifi) + } +} + +func benchmarkReadWriteIPv6UDP(b *testing.B, p *ipv6.PacketConn, wb, rb []byte, dst net.Addr, ifi *net.Interface) { + cm := ipv6.ControlMessage{ + TrafficClass: DiffServAF11 | CongestionExperienced, + HopLimit: 1, + } + if ifi != nil { + cm.IfIndex = ifi.Index + } + if n, err := p.WriteTo(wb, &cm, dst); err != nil { + b.Fatalf("ipv6.PacketConn.WriteTo failed: %v", err) + } else if n != len(wb) { + b.Fatalf("ipv6.PacketConn.WriteTo failed: short write: %v", n) + } + if _, _, _, err := p.ReadFrom(rb); err != nil { + b.Fatalf("ipv6.PacketConn.ReadFrom failed: %v", err) + } +} + +func TestPacketConnConcurrentReadWriteUnicastUDP(t *testing.T) { + switch runtime.GOOS { + case "dragonfly", "plan9", "solaris", "windows": + t.Skipf("not supported on %q", runtime.GOOS) + } + if !supportsIPv6 { + t.Skip("ipv6 is not supported") + } + + c, err := net.ListenPacket("udp6", "[::1]:0") + if err != nil { + t.Fatalf("net.ListenPacket failed: %v", err) + } + defer c.Close() + p := ipv6.NewPacketConn(c) + defer p.Close() + + dst, err := net.ResolveUDPAddr("udp6", c.LocalAddr().String()) + if err != nil { + t.Fatalf("net.ResolveUDPAddr failed: %v", err) + } + + ifi := loopbackInterface() + cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagSrc | ipv6.FlagDst | ipv6.FlagInterface | ipv6.FlagPathMTU + wb := []byte("HELLO-R-U-THERE") + + var wg sync.WaitGroup + reader := func() { + defer wg.Done() + rb := make([]byte, 128) + if n, cm, _, err := p.ReadFrom(rb); err != nil { + t.Errorf("ipv6.PacketConn.ReadFrom failed: %v", err) + return + } else if !bytes.Equal(rb[:n], wb) { + t.Errorf("got %v; expected %v", rb[:n], wb) + return + } else { + t.Logf("rcvd cmsg: %v", cm) + } + } + writer := func(toggle bool) { + defer wg.Done() + cm := ipv6.ControlMessage{ + TrafficClass: DiffServAF11 | CongestionExperienced, + Src: net.IPv6loopback, + Dst: net.IPv6loopback, + } + if ifi != nil { + cm.IfIndex = ifi.Index + } + if err := p.SetControlMessage(cf, toggle); err != nil { + t.Errorf("ipv6.PacketConn.SetControlMessage failed: %v", err) + return + } + if n, err := p.WriteTo(wb, &cm, dst); err != nil { + t.Errorf("ipv6.PacketConn.WriteTo failed: %v", err) + return + } else if n != len(wb) { + t.Errorf("ipv6.PacketConn.WriteTo failed: short write: %v", n) + return + } + } + + const N = 10 + wg.Add(N) + for i := 0; i < N; i++ { + go reader() + } + wg.Add(2 * N) + for i := 0; i < 2*N; i++ { + go writer(i%2 != 0) + } + wg.Add(N) + for i := 0; i < N; i++ { + go reader() + } + wg.Wait() +} diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/sockopt_rfc2292_darwin.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/sockopt_rfc2292_darwin.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/sockopt_rfc2292_darwin.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/sockopt_rfc2292_darwin.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ipv6 - -import ( - "os" - "syscall" -) - -func ipv6ReceiveTrafficClass(fd int) (bool, error) { - return false, errNotSupported -} - -func setIPv6ReceiveTrafficClass(fd int, v bool) error { - return errNotSupported -} - -func ipv6ReceiveHopLimit(fd int) (bool, error) { - v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_2292HOPLIMIT) - if err != nil { - return false, os.NewSyscallError("getsockopt", err) - } - return v == 1, nil -} - -func setIPv6ReceiveHopLimit(fd int, v bool) error { - return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_2292HOPLIMIT, boolint(v))) -} - -func ipv6ReceivePacketInfo(fd int) (bool, error) { - v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_2292PKTINFO) - if err != nil { - return false, os.NewSyscallError("getsockopt", err) - } - return v == 1, nil -} - -func setIPv6ReceivePacketInfo(fd int, v bool) error { - return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_2292PKTINFO, boolint(v))) -} - -func ipv6PathMTU(fd int) (int, error) { - return 0, errNotSupported -} - -func ipv6ReceivePathMTU(fd int) (bool, error) { - return false, errNotSupported -} - -func setIPv6ReceivePathMTU(fd int, v bool) error { - return errNotSupported -} - -func ipv6ICMPFilter(fd int) (*ICMPFilter, error) { - v, err := syscall.GetsockoptICMPv6Filter(fd, ianaProtocolIPv6ICMP, syscall.ICMP6_FILTER) - if err != nil { - return nil, os.NewSyscallError("getsockopt", err) - } - return &ICMPFilter{rawICMPFilter: rawICMPFilter{*v}}, nil -} - -func setIPv6ICMPFilter(fd int, f *ICMPFilter) error { - return os.NewSyscallError("setsockopt", syscall.SetsockoptICMPv6Filter(fd, ianaProtocolIPv6ICMP, syscall.ICMP6_FILTER, &f.rawICMPFilter.ICMPv6Filter)) -} diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/sockopt_rfc2292_unix.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/sockopt_rfc2292_unix.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/sockopt_rfc2292_unix.go 1970-01-01 00:00:00.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/sockopt_rfc2292_unix.go 2014-04-11 09:53:46.000000000 +0000 @@ -0,0 +1,73 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin + +package ipv6 + +import ( + "os" + "unsafe" +) + +func ipv6ReceiveTrafficClass(fd int) (bool, error) { + return false, errOpNoSupport +} + +func setIPv6ReceiveTrafficClass(fd int, v bool) error { + return errOpNoSupport +} + +func ipv6ReceiveHopLimit(fd int) (bool, error) { + var v int32 + l := sysSockoptLen(4) + if err := getsockopt(fd, ianaProtocolIPv6, sysSockopt2292HopLimit, uintptr(unsafe.Pointer(&v)), &l); err != nil { + return false, os.NewSyscallError("getsockopt", err) + } + return v == 1, nil +} + +func setIPv6ReceiveHopLimit(fd int, v bool) error { + vv := int32(boolint(v)) + return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockopt2292HopLimit, uintptr(unsafe.Pointer(&vv)), 4)) +} + +func ipv6ReceivePacketInfo(fd int) (bool, error) { + var v int32 + l := sysSockoptLen(4) + if err := getsockopt(fd, ianaProtocolIPv6, sysSockopt2292PacketInfo, uintptr(unsafe.Pointer(&v)), &l); err != nil { + return false, os.NewSyscallError("getsockopt", err) + } + return v == 1, nil +} + +func setIPv6ReceivePacketInfo(fd int, v bool) error { + vv := int32(boolint(v)) + return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockopt2292PacketInfo, uintptr(unsafe.Pointer(&vv)), 4)) +} + +func ipv6PathMTU(fd int) (int, error) { + return 0, errOpNoSupport +} + +func ipv6ReceivePathMTU(fd int) (bool, error) { + return false, errOpNoSupport +} + +func setIPv6ReceivePathMTU(fd int, v bool) error { + return errOpNoSupport +} + +func ipv6ICMPFilter(fd int) (*ICMPFilter, error) { + var v ICMPFilter + l := sysSockoptLen(sysSizeofICMPFilter) + if err := getsockopt(fd, ianaProtocolIPv6ICMP, sysSockoptICMPFilter, uintptr(unsafe.Pointer(&v.sysICMPFilter)), &l); err != nil { + return nil, os.NewSyscallError("getsockopt", err) + } + return &v, nil +} + +func setIPv6ICMPFilter(fd int, f *ICMPFilter) error { + return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6ICMP, sysSockoptICMPFilter, uintptr(unsafe.Pointer(&f.sysICMPFilter)), sysSizeofICMPFilter)) +} diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/sockopt_rfc3493_bsd.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/sockopt_rfc3493_bsd.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/sockopt_rfc3493_bsd.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/sockopt_rfc3493_bsd.go 2014-04-11 09:53:46.000000000 +0000 @@ -8,12 +8,13 @@ import ( "os" - "syscall" + "unsafe" ) func setIPv6Checksum(fd int, on bool, offset int) error { if !on { offset = -1 } - return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_CHECKSUM, offset)) + v := int32(offset) + return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptChecksum, uintptr(unsafe.Pointer(&v)), 4)) } diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/sockopt_rfc3493_linux.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/sockopt_rfc3493_linux.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/sockopt_rfc3493_linux.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/sockopt_rfc3493_linux.go 2014-04-11 09:53:46.000000000 +0000 @@ -6,12 +6,13 @@ import ( "os" - "syscall" + "unsafe" ) func setIPv6Checksum(fd int, on bool, offset int) error { if !on { offset = -1 } - return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolReserved, syscall.IPV6_CHECKSUM, offset)) + v := int32(offset) + return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolReserved, sysSockoptChecksum, uintptr(unsafe.Pointer(&v)), 4)) } diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/sockopt_rfc3493_unix.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/sockopt_rfc3493_unix.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/sockopt_rfc3493_unix.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/sockopt_rfc3493_unix.go 2014-04-11 09:53:46.000000000 +0000 @@ -9,66 +9,74 @@ import ( "net" "os" - "syscall" + "unsafe" ) func ipv6TrafficClass(fd int) (int, error) { - v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_TCLASS) - if err != nil { + var v int32 + l := sysSockoptLen(4) + if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptTrafficClass, uintptr(unsafe.Pointer(&v)), &l); err != nil { return 0, os.NewSyscallError("getsockopt", err) } - return v, nil + return int(v), nil } func setIPv6TrafficClass(fd, v int) error { - return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_TCLASS, v)) + vv := int32(v) + return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptTrafficClass, uintptr(unsafe.Pointer(&vv)), 4)) } func ipv6HopLimit(fd int) (int, error) { - v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_UNICAST_HOPS) - if err != nil { + var v int32 + l := sysSockoptLen(4) + if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptUnicastHopLimit, uintptr(unsafe.Pointer(&v)), &l); err != nil { return 0, os.NewSyscallError("getsockopt", err) } - return v, nil + return int(v), nil } func setIPv6HopLimit(fd, v int) error { - return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_UNICAST_HOPS, v)) + vv := int32(v) + return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptUnicastHopLimit, uintptr(unsafe.Pointer(&vv)), 4)) } func ipv6Checksum(fd int) (bool, int, error) { - v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_CHECKSUM) - if err != nil { + var v int32 + l := sysSockoptLen(4) + if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptChecksum, uintptr(unsafe.Pointer(&v)), &l); err != nil { return false, 0, os.NewSyscallError("getsockopt", err) } on := true if v == -1 { on = false } - return on, v, nil + return on, int(v), nil } func ipv6MulticastHopLimit(fd int) (int, error) { - v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_HOPS) - if err != nil { + var v int32 + l := sysSockoptLen(4) + if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastHopLimit, uintptr(unsafe.Pointer(&v)), &l); err != nil { return 0, os.NewSyscallError("getsockopt", err) } - return v, nil + return int(v), nil } func setIPv6MulticastHopLimit(fd, v int) error { - return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_HOPS, v)) + vv := int32(v) + return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastHopLimit, uintptr(unsafe.Pointer(&vv)), 4)) } func ipv6MulticastInterface(fd int) (*net.Interface, error) { - v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_IF) - if err != nil { + var v int32 + l := sysSockoptLen(4) + if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastInterface, uintptr(unsafe.Pointer(&v)), &l); err != nil { return nil, os.NewSyscallError("getsockopt", err) } if v == 0 { return nil, nil } - ifi, err := net.InterfaceByIndex(v) + ifi, err := net.InterfaceByIndex(int(v)) if err != nil { return nil, err } @@ -76,39 +84,41 @@ } func setIPv6MulticastInterface(fd int, ifi *net.Interface) error { - var v int + var v int32 if ifi != nil { - v = ifi.Index + v = int32(ifi.Index) } - return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_IF, v)) + return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastInterface, uintptr(unsafe.Pointer(&v)), 4)) } func ipv6MulticastLoopback(fd int) (bool, error) { - v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_LOOP) - if err != nil { + var v int32 + l := sysSockoptLen(4) + if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastLoopback, uintptr(unsafe.Pointer(&v)), &l); err != nil { return false, os.NewSyscallError("getsockopt", err) } return v == 1, nil } func setIPv6MulticastLoopback(fd int, v bool) error { - return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_LOOP, boolint(v))) + vv := int32(boolint(v)) + return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastLoopback, uintptr(unsafe.Pointer(&vv)), 4)) } func joinIPv6Group(fd int, ifi *net.Interface, grp net.IP) error { - mreq := syscall.IPv6Mreq{} - copy(mreq.Multiaddr[:], grp) + mreq := sysMulticastReq{} + copy(mreq.IP[:], grp) if ifi != nil { - mreq.Interface = uint32(ifi.Index) + mreq.IfIndex = uint32(ifi.Index) } - return os.NewSyscallError("setsockopt", syscall.SetsockoptIPv6Mreq(fd, ianaProtocolIPv6, syscall.IPV6_JOIN_GROUP, &mreq)) + return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptJoinGroup, uintptr(unsafe.Pointer(&mreq)), sysSizeofMulticastReq)) } func leaveIPv6Group(fd int, ifi *net.Interface, grp net.IP) error { - mreq := syscall.IPv6Mreq{} - copy(mreq.Multiaddr[:], grp) + mreq := sysMulticastReq{} + copy(mreq.IP[:], grp) if ifi != nil { - mreq.Interface = uint32(ifi.Index) + mreq.IfIndex = uint32(ifi.Index) } - return os.NewSyscallError("setsockopt", syscall.SetsockoptIPv6Mreq(fd, ianaProtocolIPv6, syscall.IPV6_LEAVE_GROUP, &mreq)) + return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptLeaveGroup, uintptr(unsafe.Pointer(&mreq)), sysSizeofMulticastReq)) } diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/sockopt_rfc3493_windows.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/sockopt_rfc3493_windows.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/sockopt_rfc3493_windows.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/sockopt_rfc3493_windows.go 2014-04-11 09:53:46.000000000 +0000 @@ -24,7 +24,7 @@ func ipv6HopLimit(fd syscall.Handle) (int, error) { var v int32 l := int32(4) - if err := syscall.Getsockopt(fd, ianaProtocolIPv6, syscall.IPV6_UNICAST_HOPS, (*byte)(unsafe.Pointer(&v)), &l); err != nil { + if err := syscall.Getsockopt(fd, ianaProtocolIPv6, sysSockoptUnicastHopLimit, (*byte)(unsafe.Pointer(&v)), &l); err != nil { return 0, os.NewSyscallError("getsockopt", err) } return int(v), nil @@ -32,7 +32,7 @@ func setIPv6HopLimit(fd syscall.Handle, v int) error { vv := int32(v) - return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, syscall.IPV6_UNICAST_HOPS, (*byte)(unsafe.Pointer(&vv)), 4)) + return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, sysSockoptUnicastHopLimit, (*byte)(unsafe.Pointer(&vv)), 4)) } func ipv6Checksum(fd syscall.Handle) (bool, int, error) { @@ -43,7 +43,7 @@ func ipv6MulticastHopLimit(fd syscall.Handle) (int, error) { var v int32 l := int32(4) - if err := syscall.Getsockopt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_HOPS, (*byte)(unsafe.Pointer(&v)), &l); err != nil { + if err := syscall.Getsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastHopLimit, (*byte)(unsafe.Pointer(&v)), &l); err != nil { return 0, os.NewSyscallError("getsockopt", err) } return int(v), nil @@ -51,13 +51,13 @@ func setIPv6MulticastHopLimit(fd syscall.Handle, v int) error { vv := int32(v) - return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_HOPS, (*byte)(unsafe.Pointer(&vv)), 4)) + return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastHopLimit, (*byte)(unsafe.Pointer(&vv)), 4)) } func ipv6MulticastInterface(fd syscall.Handle) (*net.Interface, error) { var v int32 l := int32(4) - if err := syscall.Getsockopt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_IF, (*byte)(unsafe.Pointer(&v)), &l); err != nil { + if err := syscall.Getsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastInterface, (*byte)(unsafe.Pointer(&v)), &l); err != nil { return nil, os.NewSyscallError("getsockopt", err) } if v == 0 { @@ -75,13 +75,13 @@ if ifi != nil { v = int32(ifi.Index) } - return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_IF, (*byte)(unsafe.Pointer(&v)), 4)) + return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastInterface, (*byte)(unsafe.Pointer(&v)), 4)) } func ipv6MulticastLoopback(fd syscall.Handle) (bool, error) { var v int32 l := int32(4) - if err := syscall.Getsockopt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_LOOP, (*byte)(unsafe.Pointer(&v)), &l); err != nil { + if err := syscall.Getsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastLoopback, (*byte)(unsafe.Pointer(&v)), &l); err != nil { return false, os.NewSyscallError("getsockopt", err) } return v == 1, nil @@ -89,25 +89,25 @@ func setIPv6MulticastLoopback(fd syscall.Handle, v bool) error { vv := int32(boolint(v)) - return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_LOOP, (*byte)(unsafe.Pointer(&vv)), 4)) + return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastLoopback, (*byte)(unsafe.Pointer(&vv)), 4)) } func joinIPv6Group(fd syscall.Handle, ifi *net.Interface, grp net.IP) error { - mreq := syscall.IPv6Mreq{} - copy(mreq.Multiaddr[:], grp) + mreq := sysMulticastReq{} + copy(mreq.IP[:], grp) if ifi != nil { - mreq.Interface = uint32(ifi.Index) + mreq.IfIndex = uint32(ifi.Index) } - return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, syscall.IPV6_JOIN_GROUP, (*byte)(unsafe.Pointer(&mreq)), int32(unsafe.Sizeof(mreq)))) + return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, sysSockoptJoinGroup, (*byte)(unsafe.Pointer(&mreq)), int32(sysSizeofMulticastReq))) } func leaveIPv6Group(fd syscall.Handle, ifi *net.Interface, grp net.IP) error { - mreq := syscall.IPv6Mreq{} - copy(mreq.Multiaddr[:], grp) + mreq := sysMulticastReq{} + copy(mreq.IP[:], grp) if ifi != nil { - mreq.Interface = uint32(ifi.Index) + mreq.IfIndex = uint32(ifi.Index) } - return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, syscall.IPV6_LEAVE_GROUP, (*byte)(unsafe.Pointer(&mreq)), int32(unsafe.Sizeof(mreq)))) + return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, sysSockoptLeaveGroup, (*byte)(unsafe.Pointer(&mreq)), int32(sysSizeofMulticastReq))) } func setIPv6Checksum(fd syscall.Handle, on bool, offset int) error { diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/sockopt_rfc3542_bsd.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/sockopt_rfc3542_bsd.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/sockopt_rfc3542_bsd.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/sockopt_rfc3542_bsd.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build freebsd netbsd openbsd - -package ipv6 - -import ( - "os" - "syscall" -) - -func ipv6PathMTU(fd int) (int, error) { - v, err := syscall.GetsockoptIPv6MTUInfo(fd, ianaProtocolIPv6, syscall.IPV6_PATHMTU) - if err != nil { - return 0, os.NewSyscallError("getsockopt", err) - } - return int(v.Mtu), nil -} - -func ipv6ReceivePathMTU(fd int) (bool, error) { - v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_RECVPATHMTU) - if err != nil { - return false, os.NewSyscallError("getsockopt", err) - } - return v == 1, nil -} - -func setIPv6ReceivePathMTU(fd int, v bool) error { - return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_RECVPATHMTU, boolint(v))) -} - -func ipv6ICMPFilter(fd int) (*ICMPFilter, error) { - v, err := syscall.GetsockoptICMPv6Filter(fd, ianaProtocolIPv6ICMP, syscall.ICMP6_FILTER) - if err != nil { - return nil, os.NewSyscallError("getsockopt", err) - } - return &ICMPFilter{rawICMPFilter: rawICMPFilter{*v}}, nil -} - -func setIPv6ICMPFilter(fd int, f *ICMPFilter) error { - return os.NewSyscallError("setsockopt", syscall.SetsockoptICMPv6Filter(fd, ianaProtocolIPv6ICMP, syscall.ICMP6_FILTER, &f.rawICMPFilter.ICMPv6Filter)) -} diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/sockopt_rfc3542_linux.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/sockopt_rfc3542_linux.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/sockopt_rfc3542_linux.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/sockopt_rfc3542_linux.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,42 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ipv6 - -import ( - "os" - "syscall" -) - -func ipv6PathMTU(fd int) (int, error) { - v, err := syscall.GetsockoptIPv6MTUInfo(fd, ianaProtocolIPv6, syscall_IPV6_PATHMTU) - if err != nil { - return 0, os.NewSyscallError("getsockopt", err) - } - return int(v.Mtu), nil -} - -func ipv6ReceivePathMTU(fd int) (bool, error) { - v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall_IPV6_RECVPATHMTU) - if err != nil { - return false, os.NewSyscallError("getsockopt", err) - } - return v == 1, nil -} - -func setIPv6ReceivePathMTU(fd int, v bool) error { - return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall_IPV6_RECVPATHMTU, boolint(v))) -} - -func ipv6ICMPFilter(fd int) (*ICMPFilter, error) { - v, err := syscall.GetsockoptICMPv6Filter(fd, ianaProtocolIPv6ICMP, syscall.ICMPV6_FILTER) - if err != nil { - return nil, os.NewSyscallError("getsockopt", err) - } - return &ICMPFilter{rawICMPFilter: rawICMPFilter{*v}}, nil -} - -func setIPv6ICMPFilter(fd int, f *ICMPFilter) error { - return os.NewSyscallError("setsockopt", syscall.SetsockoptICMPv6Filter(fd, ianaProtocolIPv6ICMP, syscall.ICMPV6_FILTER, &f.rawICMPFilter.ICMPv6Filter)) -} diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/sockopt_rfc3542_plan9.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/sockopt_rfc3542_plan9.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/sockopt_rfc3542_plan9.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/sockopt_rfc3542_plan9.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ipv6 - -import "syscall" - -func ipv6PathMTU(fd int) (int, error) { - // TODO(mikio): Implement this - return 0, syscall.EPLAN9 -} diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/sockopt_rfc3542_stub.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/sockopt_rfc3542_stub.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/sockopt_rfc3542_stub.go 1970-01-01 00:00:00.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/sockopt_rfc3542_stub.go 2014-04-11 09:53:46.000000000 +0000 @@ -0,0 +1,12 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build dragonfly plan9 solaris + +package ipv6 + +func ipv6PathMTU(fd int) (int, error) { + // TODO(mikio): Implement this + return 0, errOpNoSupport +} diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/sockopt_rfc3542_unix.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/sockopt_rfc3542_unix.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/sockopt_rfc3542_unix.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/sockopt_rfc3542_unix.go 2014-04-11 09:53:46.000000000 +0000 @@ -8,41 +8,83 @@ import ( "os" - "syscall" + "unsafe" ) func ipv6ReceiveTrafficClass(fd int) (bool, error) { - v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_RECVTCLASS) - if err != nil { + var v int32 + l := sysSockoptLen(4) + if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptReceiveTrafficClass, uintptr(unsafe.Pointer(&v)), &l); err != nil { return false, os.NewSyscallError("getsockopt", err) } return v == 1, nil } func setIPv6ReceiveTrafficClass(fd int, v bool) error { - return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_RECVTCLASS, boolint(v))) + vv := int32(boolint(v)) + return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptReceiveTrafficClass, uintptr(unsafe.Pointer(&vv)), 4)) } func ipv6ReceiveHopLimit(fd int) (bool, error) { - v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_RECVHOPLIMIT) - if err != nil { + var v int32 + l := sysSockoptLen(4) + if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptReceiveHopLimit, uintptr(unsafe.Pointer(&v)), &l); err != nil { return false, os.NewSyscallError("getsockopt", err) } return v == 1, nil } func setIPv6ReceiveHopLimit(fd int, v bool) error { - return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_RECVHOPLIMIT, boolint(v))) + vv := int32(boolint(v)) + return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptReceiveHopLimit, uintptr(unsafe.Pointer(&vv)), 4)) } func ipv6ReceivePacketInfo(fd int) (bool, error) { - v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_RECVPKTINFO) - if err != nil { + var v int32 + l := sysSockoptLen(4) + if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptReceivePacketInfo, uintptr(unsafe.Pointer(&v)), &l); err != nil { return false, os.NewSyscallError("getsockopt", err) } return v == 1, nil } func setIPv6ReceivePacketInfo(fd int, v bool) error { - return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_RECVPKTINFO, boolint(v))) + vv := int32(boolint(v)) + return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptReceivePacketInfo, uintptr(unsafe.Pointer(&vv)), 4)) +} + +func ipv6PathMTU(fd int) (int, error) { + var v sysMTUInfo + l := sysSockoptLen(sysSizeofMTUInfo) + if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptPathMTU, uintptr(unsafe.Pointer(&v)), &l); err != nil { + return 0, os.NewSyscallError("getsockopt", err) + } + return int(v.MTU), nil +} + +func ipv6ReceivePathMTU(fd int) (bool, error) { + var v int32 + l := sysSockoptLen(4) + if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptReceivePathMTU, uintptr(unsafe.Pointer(&v)), &l); err != nil { + return false, os.NewSyscallError("getsockopt", err) + } + return v == 1, nil +} + +func setIPv6ReceivePathMTU(fd int, v bool) error { + vv := int32(boolint(v)) + return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptReceivePathMTU, uintptr(unsafe.Pointer(&vv)), 4)) +} + +func ipv6ICMPFilter(fd int) (*ICMPFilter, error) { + var v ICMPFilter + l := sysSockoptLen(sysSizeofICMPFilter) + if err := getsockopt(fd, ianaProtocolIPv6ICMP, sysSockoptICMPFilter, uintptr(unsafe.Pointer(&v.sysICMPFilter)), &l); err != nil { + return nil, os.NewSyscallError("getsockopt", err) + } + return &v, nil +} + +func setIPv6ICMPFilter(fd int, f *ICMPFilter) error { + return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6ICMP, sysSockoptICMPFilter, uintptr(unsafe.Pointer(&f.sysICMPFilter)), sysSizeofICMPFilter)) } diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/sockopt_test.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/sockopt_test.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/sockopt_test.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/sockopt_test.go 2014-04-11 09:53:46.000000000 +0000 @@ -24,7 +24,7 @@ var condFatalf = func() func(*testing.T, string, ...interface{}) { // A few APIs are not implemented yet on some platforms. switch runtime.GOOS { - case "darwin", "plan9", "windows": + case "darwin", "dragonfly", "plan9", "solaris", "windows": return (*testing.T).Logf } return (*testing.T).Fatalf @@ -32,7 +32,7 @@ func TestConnInitiatorPathMTU(t *testing.T) { switch runtime.GOOS { - case "plan9", "windows": + case "dragonfly", "plan9", "solaris", "windows": t.Skipf("not supported on %q", runtime.GOOS) } if !supportsIPv6 { @@ -65,7 +65,7 @@ func TestConnResponderPathMTU(t *testing.T) { switch runtime.GOOS { - case "plan9", "windows": + case "dragonfly", "plan9", "solaris", "windows": t.Skipf("not supported on %q", runtime.GOOS) } if !supportsIPv6 { @@ -98,7 +98,7 @@ func TestPacketConnChecksum(t *testing.T) { switch runtime.GOOS { - case "plan9", "windows": + case "dragonfly", "plan9", "solaris", "windows": t.Skipf("not supported on %q", runtime.GOOS) } if !supportsIPv6 { diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/sys_bsd.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/sys_bsd.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/sys_bsd.go 1970-01-01 00:00:00.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/sys_bsd.go 2014-04-11 09:53:46.000000000 +0000 @@ -0,0 +1,48 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build freebsd netbsd openbsd + +package ipv6 + +import ( + "net" + "syscall" +) + +// RFC 3493 options +const ( + // See /usr/include/netinet6/in6.h. + sysSockoptUnicastHopLimit = 0x4 + sysSockoptMulticastHopLimit = 0xa + sysSockoptMulticastInterface = 0x9 + sysSockoptMulticastLoopback = 0xb + sysSockoptJoinGroup = 0xc + sysSockoptLeaveGroup = 0xd +) + +// RFC 3542 options +const ( + // See /usr/include/netinet6/in6.h. + sysSockoptReceiveTrafficClass = 0x39 + sysSockoptTrafficClass = 0x3d + sysSockoptReceiveHopLimit = 0x25 + sysSockoptHopLimit = 0x2f + sysSockoptReceivePacketInfo = 0x24 + sysSockoptPacketInfo = 0x2e + sysSockoptReceivePathMTU = 0x2b + sysSockoptPathMTU = 0x2c + sysSockoptNextHop = 0x30 + sysSockoptChecksum = 0x1a + + // See /usr/include/netinet6/in6.h. + sysSockoptICMPFilter = 0x12 +) + +func setSockaddr(sa *syscall.RawSockaddrInet6, ip net.IP, ifindex int) { + sa.Len = syscall.SizeofSockaddrInet6 + sa.Family = syscall.AF_INET6 + copy(sa.Addr[:], ip) + sa.Scope_id = uint32(ifindex) +} diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/syscall_linux_386.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/syscall_linux_386.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/syscall_linux_386.go 1970-01-01 00:00:00.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/syscall_linux_386.go 2014-04-11 09:53:46.000000000 +0000 @@ -0,0 +1,42 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This code is a duplicate of syscall/syscall_linux_386.go with small +// modifications. + +package ipv6 + +import ( + "syscall" + "unsafe" +) + +// On x86 Linux, all the socket calls go through an extra indirection, +// I think because the 5-register system call interface can't handle +// the 6-argument calls like sendto and recvfrom. Instead the +// arguments to the underlying system call are the number below and a +// pointer to an array of uintptr. We hide the pointer in the +// socketcall assembly to avoid allocation on every system call. + +const ( + // See /usr/include/linux/net.h. + _SETSOCKOPT = 14 + _GETSOCKOPT = 15 +) + +var socketcall func(call int, a0, a1, a2, a3, a4, a5 uintptr) (int, syscall.Errno) + +func getsockopt(fd int, level int, name int, v uintptr, l *sysSockoptLen) error { + if _, errno := socketcall(_GETSOCKOPT, uintptr(fd), uintptr(level), uintptr(name), uintptr(v), uintptr(unsafe.Pointer(l)), 0); errno != 0 { + return error(errno) + } + return nil +} + +func setsockopt(fd int, level int, name int, v uintptr, l uintptr) error { + if _, errno := socketcall(_SETSOCKOPT, uintptr(fd), uintptr(level), uintptr(name), v, l, 0); errno != 0 { + return error(errno) + } + return nil +} diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/syscall_linux_386.s juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/syscall_linux_386.s --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/syscall_linux_386.s 1970-01-01 00:00:00.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/syscall_linux_386.s 2014-04-11 09:53:46.000000000 +0000 @@ -0,0 +1,56 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This code is a duplicate of syscall/syscall_linux_386.s with small +// modifications. + +#define SYS_SOCKETCALL 102 // from zsysnum_linux_386.go + +// func socketcallnosplit7(call int, a0, a1, a2, a3, a4, a5 uintptr) (n int, errno int) +// Kernel interface gets call sub-number and pointer to a0 for Go 1.1. +TEXT ·socketcallnosplit7(SB),7,$0 + CALL runtime·entersyscall(SB) + MOVL $SYS_SOCKETCALL, AX // syscall entry + MOVL 4(SP), BX // socket call number + LEAL 8(SP), CX // pointer to call arguments + MOVL $0, DX + MOVL $0, SI + MOVL $0, DI + CALL *runtime·_vdso(SB) + CMPL AX, $0xfffff001 + JLS ok1 + MOVL $-1, 32(SP) // n + NEGL AX + MOVL AX, 36(SP) // errno + CALL runtime·exitsyscall(SB) + RET +ok1: + MOVL AX, 32(SP) // n + MOVL $0, 36(SP) // errno + CALL runtime·exitsyscall(SB) + RET + +// func socketcallnosplit4(call int, a0, a1, a2, a3, a4, a5 uintptr) (n int, errno int) +// Kernel interface gets call sub-number and pointer to a0 for Go 1.2. +TEXT ·socketcallnosplit4(SB),4,$0-40 + CALL runtime·entersyscall(SB) + MOVL $SYS_SOCKETCALL, AX // syscall entry + MOVL 4(SP), BX // socket call number + LEAL 8(SP), CX // pointer to call arguments + MOVL $0, DX + MOVL $0, SI + MOVL $0, DI + CALL *runtime·_vdso(SB) + CMPL AX, $0xfffff001 + JLS ok2 + MOVL $-1, 32(SP) // n + NEGL AX + MOVL AX, 36(SP) // errno + CALL runtime·exitsyscall(SB) + RET +ok2: + MOVL AX, 32(SP) // n + MOVL $0, 36(SP) // errno + CALL runtime·exitsyscall(SB) + RET diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/syscall_nosplit4_linux_386.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/syscall_nosplit4_linux_386.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/syscall_nosplit4_linux_386.go 1970-01-01 00:00:00.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/syscall_nosplit4_linux_386.go 2014-04-11 09:53:46.000000000 +0000 @@ -0,0 +1,15 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.2 + +package ipv6 + +import "syscall" + +func socketcallnosplit4(call int, a0, a1, a2, a3, a4, a5 uintptr) (int, syscall.Errno) + +func init() { + socketcall = socketcallnosplit4 +} diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/syscall_nosplit7_linux_386.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/syscall_nosplit7_linux_386.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/syscall_nosplit7_linux_386.go 1970-01-01 00:00:00.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/syscall_nosplit7_linux_386.go 2014-04-11 09:53:46.000000000 +0000 @@ -0,0 +1,15 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.1,!go1.2 + +package ipv6 + +import "syscall" + +func socketcallnosplit7(call int, a0, a1, a2, a3, a4, a5 uintptr) (int, syscall.Errno) + +func init() { + socketcall = socketcallnosplit7 +} diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/syscall_unix.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/syscall_unix.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/syscall_unix.go 1970-01-01 00:00:00.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/syscall_unix.go 2014-04-11 09:53:46.000000000 +0000 @@ -0,0 +1,26 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin freebsd linux,amd64 linux,arm netbsd openbsd + +package ipv6 + +import ( + "syscall" + "unsafe" +) + +func getsockopt(fd int, level, name int, v uintptr, l *sysSockoptLen) error { + if _, _, errno := syscall.Syscall6(syscall.SYS_GETSOCKOPT, uintptr(fd), uintptr(level), uintptr(name), uintptr(v), uintptr(unsafe.Pointer(l)), 0); errno != 0 { + return error(errno) + } + return nil +} + +func setsockopt(fd int, level int, name int, v uintptr, l uintptr) error { + if _, _, errno := syscall.Syscall6(syscall.SYS_SETSOCKOPT, uintptr(fd), uintptr(level), uintptr(name), uintptr(v), uintptr(l), 0); errno != 0 { + return error(errno) + } + return nil +} diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/sys_darwin.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/sys_darwin.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/sys_darwin.go 1970-01-01 00:00:00.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/sys_darwin.go 2014-04-11 09:53:46.000000000 +0000 @@ -0,0 +1,54 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv6 + +import ( + "net" + "syscall" +) + +// RFC 2292 options +const ( + // See /usr/include/netinet6/in6.h. + sysSockopt2292HopLimit = 0x14 + sysSockopt2292PacketInfo = 0x13 + sysSockopt2292NextHop = 0x15 +) + +// RFC 3493 options +const ( + // See /usr/include/netinet6/in6.h. + sysSockoptUnicastHopLimit = 0x4 + sysSockoptMulticastHopLimit = 0xa + sysSockoptMulticastInterface = 0x9 + sysSockoptMulticastLoopback = 0xb + sysSockoptJoinGroup = 0xc + sysSockoptLeaveGroup = 0xd +) + +// RFC 3542 options +const ( + // See /usr/include/netinet6/in6.h. + sysSockoptReceiveTrafficClass = 0x23 + sysSockoptTrafficClass = 0x24 + sysSockoptReceiveHopLimit = 0x25 + sysSockoptHopLimit = 0x2f + sysSockoptReceivePacketInfo = 0x3d + sysSockoptPacketInfo = 0x2e + sysSockoptReceivePathMTU = 0x2b + sysSockoptPathMTU = 0x2c + sysSockoptNextHop = 0x30 + sysSockoptChecksum = 0x1a + + // See /usr/include/netinet6/in6.h. + sysSockoptICMPFilter = 0x12 +) + +func setSockaddr(sa *syscall.RawSockaddrInet6, ip net.IP, ifindex int) { + sa.Len = syscall.SizeofSockaddrInet6 + sa.Family = syscall.AF_INET6 + copy(sa.Addr[:], ip) + sa.Scope_id = uint32(ifindex) +} diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/sys.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/sys.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/sys.go 1970-01-01 00:00:00.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/sys.go 2014-04-11 09:53:46.000000000 +0000 @@ -0,0 +1,23 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv6 + +type sysSockoptLen uint32 + +const ( + sysSizeofPacketInfo = 0x14 + sysSizeofMulticastReq = 0x14 + sysSizeofICMPFilter = 0x20 +) + +type sysPacketInfo struct { + IP [16]byte + IfIndex uint32 +} + +type sysMulticastReq struct { + IP [16]byte + IfIndex uint32 +} diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/sys_linux.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/sys_linux.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/sys_linux.go 1970-01-01 00:00:00.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/sys_linux.go 2014-04-11 09:53:46.000000000 +0000 @@ -0,0 +1,45 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv6 + +import ( + "net" + "syscall" +) + +// RFC 3493 options +const ( + // See /usr/include/linux/in6.h. + sysSockoptUnicastHopLimit = 0x10 + sysSockoptMulticastHopLimit = 0x12 + sysSockoptMulticastInterface = 0x11 + sysSockoptMulticastLoopback = 0x13 + sysSockoptJoinGroup = 0x14 + sysSockoptLeaveGroup = 0x15 +) + +// RFC 3542 options +const ( + // See /usr/include/linux/ipv6.h,in6.h. + sysSockoptReceiveTrafficClass = 0x42 + sysSockoptTrafficClass = 0x43 + sysSockoptReceiveHopLimit = 0x33 + sysSockoptHopLimit = 0x34 + sysSockoptReceivePacketInfo = 0x31 + sysSockoptPacketInfo = 0x32 + sysSockoptReceivePathMTU = 0x3c + sysSockoptPathMTU = 0x3d + sysSockoptNextHop = 0x9 + sysSockoptChecksum = 0x7 + + // See /usr/include/linux/icmpv6.h. + sysSockoptICMPFilter = 0x1 +) + +func setSockaddr(sa *syscall.RawSockaddrInet6, ip net.IP, ifindex int) { + sa.Family = syscall.AF_INET6 + copy(sa.Addr[:], ip) + sa.Scope_id = uint32(ifindex) +} diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/sys_unix.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/sys_unix.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/sys_unix.go 1970-01-01 00:00:00.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/sys_unix.go 2014-04-11 09:53:46.000000000 +0000 @@ -0,0 +1,16 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin freebsd linux netbsd openbsd + +package ipv6 + +import "syscall" + +const sysSizeofMTUInfo = 0x20 + +type sysMTUInfo struct { + Addr syscall.RawSockaddrInet6 + MTU uint32 +} diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/sys_windows.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/sys_windows.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/sys_windows.go 1970-01-01 00:00:00.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/sys_windows.go 2014-04-11 09:53:46.000000000 +0000 @@ -0,0 +1,33 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv6 + +import ( + "net" + "syscall" +) + +// RFC 3493 options +const ( + // See ws2tcpip.h. + sysSockoptUnicastHopLimit = 0x4 + sysSockoptMulticastHopLimit = 0xa + sysSockoptMulticastInterface = 0x9 + sysSockoptMulticastLoopback = 0xb + sysSockoptJoinGroup = 0xc + sysSockoptLeaveGroup = 0xd +) + +// RFC 3542 options +const ( + // See ws2tcpip.h. + sysSockoptPacketInfo = 0x13 +) + +func setSockaddr(sa *syscall.RawSockaddrInet6, ip net.IP, ifindex int) { + sa.Family = syscall.AF_INET6 + copy(sa.Addr[:], ip) + sa.Scope_id = uint32(ifindex) +} diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/unicastsockopt_test.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/unicastsockopt_test.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/unicastsockopt_test.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/unicastsockopt_test.go 2014-04-11 09:53:46.000000000 +0000 @@ -14,7 +14,7 @@ func TestConnUnicastSocketOptions(t *testing.T) { switch runtime.GOOS { - case "plan9", "windows": + case "dragonfly", "plan9", "solaris", "windows": t.Skipf("not supported on %q", runtime.GOOS) } if !supportsIPv6 { @@ -50,7 +50,7 @@ func TestPacketConnUnicastSocketOptions(t *testing.T) { switch runtime.GOOS { - case "plan9", "windows": + case "dragonfly", "plan9", "solaris", "windows": t.Skipf("not supported on %q", runtime.GOOS) } if !supportsIPv6 { diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/unicast_test.go juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/unicast_test.go --- juju-core-1.18.0/src/code.google.com/p/go.net/ipv6/unicast_test.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/ipv6/unicast_test.go 2014-04-11 09:53:46.000000000 +0000 @@ -5,89 +5,18 @@ package ipv6_test import ( + "bytes" "code.google.com/p/go.net/ipv6" "net" "os" "runtime" "testing" + "time" ) -func benchmarkUDPListener() (net.PacketConn, net.Addr, error) { - c, err := net.ListenPacket("udp6", "[::1]:0") - if err != nil { - return nil, nil, err - } - dst, err := net.ResolveUDPAddr("udp6", c.LocalAddr().String()) - if err != nil { - c.Close() - return nil, nil, err - } - return c, dst, nil -} - -func BenchmarkReadWriteNetUDP(b *testing.B) { - c, dst, err := benchmarkUDPListener() - if err != nil { - b.Fatalf("benchmarkUDPListener failed: %v", err) - } - defer c.Close() - - wb, rb := []byte("HELLO-R-U-THERE"), make([]byte, 128) - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchmarkReadWriteNetUDP(b, c, wb, rb, dst) - } -} - -func benchmarkReadWriteNetUDP(b *testing.B, c net.PacketConn, wb, rb []byte, dst net.Addr) { - if _, err := c.WriteTo(wb, dst); err != nil { - b.Fatalf("net.PacketConn.WriteTo failed: %v", err) - } - if _, _, err := c.ReadFrom(rb); err != nil { - b.Fatalf("net.PacketConn.ReadFrom failed: %v", err) - } -} - -func BenchmarkReadWriteIPv6UDP(b *testing.B) { - c, dst, err := benchmarkUDPListener() - if err != nil { - b.Fatalf("benchmarkUDPListener failed: %v", err) - } - defer c.Close() - - p := ipv6.NewPacketConn(c) - cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagInterface | ipv6.FlagPathMTU - if err := p.SetControlMessage(cf, true); err != nil { - b.Fatalf("ipv6.PacketConn.SetControlMessage failed: %v", err) - } - ifi := loopbackInterface() - - wb, rb := []byte("HELLO-R-U-THERE"), make([]byte, 128) - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchmarkReadWriteIPv6UDP(b, p, wb, rb, dst, ifi) - } -} - -func benchmarkReadWriteIPv6UDP(b *testing.B, p *ipv6.PacketConn, wb, rb []byte, dst net.Addr, ifi *net.Interface) { - cm := ipv6.ControlMessage{ - TrafficClass: DiffServAF11 | CongestionExperienced, - HopLimit: 1, - } - if ifi != nil { - cm.IfIndex = ifi.Index - } - if _, err := p.WriteTo(wb, &cm, dst); err != nil { - b.Fatalf("ipv6.PacketConn.WriteTo failed: %v", err) - } - if _, _, _, err := p.ReadFrom(rb); err != nil { - b.Fatalf("ipv6.PacketConn.ReadFrom failed: %v", err) - } -} - func TestPacketConnReadWriteUnicastUDP(t *testing.T) { switch runtime.GOOS { - case "plan9", "windows": + case "dragonfly", "plan9", "solaris", "windows": t.Skipf("not supported on %q", runtime.GOOS) } if !supportsIPv6 { @@ -99,33 +28,47 @@ t.Fatalf("net.ListenPacket failed: %v", err) } defer c.Close() + p := ipv6.NewPacketConn(c) + defer p.Close() dst, err := net.ResolveUDPAddr("udp6", c.LocalAddr().String()) if err != nil { t.Fatalf("net.ResolveUDPAddr failed: %v", err) } - p := ipv6.NewPacketConn(c) cm := ipv6.ControlMessage{ TrafficClass: DiffServAF11 | CongestionExperienced, + Src: net.IPv6loopback, + Dst: net.IPv6loopback, } - cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagInterface | ipv6.FlagPathMTU + cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagSrc | ipv6.FlagDst | ipv6.FlagInterface | ipv6.FlagPathMTU ifi := loopbackInterface() if ifi != nil { cm.IfIndex = ifi.Index } + wb := []byte("HELLO-R-U-THERE") for i, toggle := range []bool{true, false, true} { if err := p.SetControlMessage(cf, toggle); err != nil { t.Fatalf("ipv6.PacketConn.SetControlMessage failed: %v", err) } cm.HopLimit = i + 1 - if _, err := p.WriteTo([]byte("HELLO-R-U-THERE"), &cm, dst); err != nil { + if err := p.SetWriteDeadline(time.Now().Add(100 * time.Millisecond)); err != nil { + t.Fatalf("ipv6.PacketConn.SetWriteDeadline failed: %v", err) + } + if n, err := p.WriteTo(wb, &cm, dst); err != nil { t.Fatalf("ipv6.PacketConn.WriteTo failed: %v", err) + } else if n != len(wb) { + t.Fatalf("ipv6.PacketConn.WriteTo failed: short write: %v", n) + } + rb := make([]byte, 128) + if err := p.SetReadDeadline(time.Now().Add(100 * time.Millisecond)); err != nil { + t.Fatalf("ipv6.PacketConn.SetReadDeadline failed: %v", err) } - b := make([]byte, 128) - if _, cm, _, err := p.ReadFrom(b); err != nil { + if n, cm, _, err := p.ReadFrom(rb); err != nil { t.Fatalf("ipv6.PacketConn.ReadFrom failed: %v", err) + } else if !bytes.Equal(rb[:n], wb) { + t.Fatalf("got %v; expected %v", rb[:n], wb) } else { t.Logf("rcvd cmsg: %v", cm) } @@ -134,7 +77,7 @@ func TestPacketConnReadWriteUnicastICMP(t *testing.T) { switch runtime.GOOS { - case "plan9", "windows": + case "dragonfly", "plan9", "solaris", "windows": t.Skipf("not supported on %q", runtime.GOOS) } if !supportsIPv6 { @@ -149,15 +92,21 @@ t.Fatalf("net.ListenPacket failed: %v", err) } defer c.Close() + p := ipv6.NewPacketConn(c) + defer p.Close() dst, err := net.ResolveIPAddr("ip6", "::1") if err != nil { t.Fatalf("net.ResolveIPAddr failed: %v", err) } - p := ipv6.NewPacketConn(c) - cm := ipv6.ControlMessage{TrafficClass: DiffServAF11 | CongestionExperienced} - cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagInterface | ipv6.FlagPathMTU + pshicmp := ipv6PseudoHeader(c.LocalAddr().(*net.IPAddr).IP, dst.IP, ianaProtocolIPv6ICMP) + cm := ipv6.ControlMessage{ + TrafficClass: DiffServAF11 | CongestionExperienced, + Src: net.IPv6loopback, + Dst: net.IPv6loopback, + } + cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagSrc | ipv6.FlagDst | ipv6.FlagInterface | ipv6.FlagPathMTU ifi := loopbackInterface() if ifi != nil { cm.IfIndex = ifi.Index @@ -170,14 +119,26 @@ t.Fatalf("ipv6.PacketConn.SetICMPFilter failed: %v", err) } + var psh []byte for i, toggle := range []bool{true, false, true} { + if toggle { + psh = nil + if err := p.SetChecksum(true, 2); err != nil { + t.Fatalf("ipv6.PacketConn.SetChecksum failed: %v", err) + } + } else { + psh = pshicmp + // Some platforms never allow to disable the + // kernel checksum processing. + p.SetChecksum(false, -1) + } wb, err := (&icmpMessage{ Type: ipv6.ICMPTypeEchoRequest, Code: 0, Body: &icmpEcho{ ID: os.Getpid() & 0xffff, Seq: i + 1, Data: []byte("HELLO-R-U-THERE"), }, - }).Marshal() + }).Marshal(psh) if err != nil { t.Fatalf("icmpMessage.Marshal failed: %v", err) } @@ -185,15 +146,23 @@ t.Fatalf("ipv6.PacketConn.SetControlMessage failed: %v", err) } cm.HopLimit = i + 1 - if _, err := p.WriteTo(wb, &cm, dst); err != nil { + if err := p.SetWriteDeadline(time.Now().Add(100 * time.Millisecond)); err != nil { + t.Fatalf("ipv6.PacketConn.SetWriteDeadline failed: %v", err) + } + if n, err := p.WriteTo(wb, &cm, dst); err != nil { t.Fatalf("ipv6.PacketConn.WriteTo failed: %v", err) + } else if n != len(wb) { + t.Fatalf("ipv6.PacketConn.WriteTo failed: short write: %v", n) + } + rb := make([]byte, 128) + if err := p.SetReadDeadline(time.Now().Add(100 * time.Millisecond)); err != nil { + t.Fatalf("ipv6.PacketConn.SetReadDeadline failed: %v", err) } - b := make([]byte, 128) - if n, cm, _, err := p.ReadFrom(b); err != nil { + if n, cm, _, err := p.ReadFrom(rb); err != nil { t.Fatalf("ipv6.PacketConn.ReadFrom failed: %v", err) } else { t.Logf("rcvd cmsg: %v", cm) - if m, err := parseICMPMessage(b[:n]); err != nil { + if m, err := parseICMPMessage(rb[:n]); err != nil { t.Fatalf("parseICMPMessage failed: %v", err) } else if m.Type != ipv6.ICMPTypeEchoReply || m.Code != 0 { t.Fatalf("got type=%v, code=%v; expected type=%v, code=%v", m.Type, m.Code, ipv6.ICMPTypeEchoReply, 0) diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/netutil/listen.go juju-core-1.18.1/src/code.google.com/p/go.net/netutil/listen.go --- juju-core-1.18.0/src/code.google.com/p/go.net/netutil/listen.go 1970-01-01 00:00:00.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/netutil/listen.go 2014-04-11 09:53:46.000000000 +0000 @@ -0,0 +1,50 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package netutil provides network utility functions, complementing the more +// common ones in the net package. +package netutil + +import ( + "net" + "sync" +) + +// LimitListener returns a Listener that accepts at most n simultaneous +// connections from the provided Listener. +func LimitListener(l net.Listener, n int) net.Listener { + ch := make(chan struct{}, n) + for i := 0; i < n; i++ { + ch <- struct{}{} + } + return &limitListener{l, ch} +} + +type limitListener struct { + net.Listener + ch chan struct{} +} + +func (l *limitListener) Accept() (net.Conn, error) { + <-l.ch + c, err := l.Listener.Accept() + if err != nil { + return nil, err + } + return &limitListenerConn{Conn: c, ch: l.ch}, nil +} + +type limitListenerConn struct { + net.Conn + ch chan<- struct{} + close sync.Once +} + +func (l *limitListenerConn) Close() error { + err := l.Conn.Close() + l.close.Do(func() { + l.ch <- struct{}{} + }) + return err +} diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/netutil/listen_test.go juju-core-1.18.1/src/code.google.com/p/go.net/netutil/listen_test.go --- juju-core-1.18.0/src/code.google.com/p/go.net/netutil/listen_test.go 1970-01-01 00:00:00.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/netutil/listen_test.go 2014-04-11 09:53:46.000000000 +0000 @@ -0,0 +1,66 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package netutil + +import ( + "fmt" + "io" + "io/ioutil" + "net" + "net/http" + "sync" + "sync/atomic" + "testing" + "time" +) + +func TestLimitListener(t *testing.T) { + const ( + max = 5 + num = 200 + ) + + l, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatalf("Listen: %v", err) + } + defer l.Close() + l = LimitListener(l, max) + + var open int32 + go http.Serve(l, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if n := atomic.AddInt32(&open, 1); n > max { + t.Errorf("%d open connections, want <= %d", n, max) + } + defer atomic.AddInt32(&open, -1) + time.Sleep(10 * time.Millisecond) + fmt.Fprint(w, "some body") + })) + + var wg sync.WaitGroup + var failed int32 + for i := 0; i < num; i++ { + wg.Add(1) + go func() { + defer wg.Done() + c := http.Client{Timeout: 3 * time.Second} + r, err := c.Get("http://" + l.Addr().String()) + if err != nil { + t.Logf("Get: %v", err) + atomic.AddInt32(&failed, 1) + return + } + defer r.Body.Close() + io.Copy(ioutil.Discard, r.Body) + }() + } + wg.Wait() + + // We expect some Gets to fail as the kernel's accept queue is filled, + // but most should succeed. + if failed >= num/2 { + t.Errorf("too many Gets failed: %v", failed) + } +} diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/proxy/proxy.go juju-core-1.18.1/src/code.google.com/p/go.net/proxy/proxy.go --- juju-core-1.18.0/src/code.google.com/p/go.net/proxy/proxy.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/proxy/proxy.go 2014-04-11 09:53:46.000000000 +0000 @@ -24,7 +24,7 @@ User, Password string } -// DefaultDialer returns the dialer specified by the proxy related variables in +// FromEnvironment returns the dialer specified by the proxy related variables in // the environment. func FromEnvironment() Dialer { allProxy := os.Getenv("all_proxy") diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/publicsuffix/gen.go juju-core-1.18.1/src/code.google.com/p/go.net/publicsuffix/gen.go --- juju-core-1.18.0/src/code.google.com/p/go.net/publicsuffix/gen.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/publicsuffix/gen.go 2014-04-11 09:53:46.000000000 +0000 @@ -28,6 +28,7 @@ "io" "net/http" "os" + "regexp" "sort" "strings" @@ -46,6 +47,28 @@ childrenBitsLo = 14 ) +var ( + maxChildren int + maxTextOffset int + maxTextLength int + maxHi uint32 + maxLo uint32 +) + +func max(a, b int) int { + if a < b { + return b + } + return a +} + +func u32max(a, b uint32) uint32 { + if a < b { + return b + } + return a +} + const ( nodeTypeNormal = 0 nodeTypeException = 1 @@ -71,6 +94,11 @@ labelsMap = map[string]bool{} rules = []string{} + // validSuffix is used to check that the entries in the public suffix list + // are in canonical form (after Punycode encoding). Specifically, capital + // letters are not allowed. + validSuffix = regexp.MustCompile(`^[a-z0-9_\!\*\-\.]+$`) + crush = flag.Bool("crush", true, "make the generated node text as small as possible") subset = flag.Bool("subset", false, "generate only a subset of the full table, for debugging") url = flag.String("url", @@ -140,6 +168,9 @@ if err != nil { return err } + if !validSuffix.MatchString(s) { + return fmt.Errorf("bad publicsuffix.org list data: %q", s) + } if *subset { switch { @@ -203,6 +234,14 @@ return err } + if *v { + fmt.Fprintf(os.Stderr, "max children %d (capacity %d)\n", maxChildren, 1<= 1<= 1<= 1<= 1<= 1< 0 { - if len(config.Protocol) != 1 { - return ErrBadWebSocketProtocol - } - fields = append(fields, "Sec-WebSocket-Protocol: "+config.Protocol[0]+"\r\n") - } - // TODO(ukai): Step 15. send cookie if any. - - // Step 16-23. generate keys and push Sec-WebSocket-Key in fields. - key1, number1 := generateKeyNumber() - key2, number2 := generateKeyNumber() - if config.handshakeData != nil { - key1 = config.handshakeData["key1"] - n, err := strconv.ParseUint(config.handshakeData["number1"], 10, 32) - if err != nil { - panic(err) - } - number1 = uint32(n) - key2 = config.handshakeData["key2"] - n, err = strconv.ParseUint(config.handshakeData["number2"], 10, 32) - if err != nil { - panic(err) - } - number2 = uint32(n) - } - fields = append(fields, "Sec-WebSocket-Key1: "+key1+"\r\n") - fields = append(fields, "Sec-WebSocket-Key2: "+key2+"\r\n") - - // Step 24. shuffle fields and send them out. - for i := 1; i < len(fields); i++ { - j := rand.Intn(i) - fields[i], fields[j] = fields[j], fields[i] - } - for i := 0; i < len(fields); i++ { - bw.WriteString(fields[i]) - } - // Step 25. send CRLF. - bw.WriteString("\r\n") - - // Step 26. generate 8 bytes random key. - key3 := generateKey3() - if config.handshakeData != nil { - key3 = []byte(config.handshakeData["key3"]) - } - // Step 27. send it out. - bw.Write(key3) - if err = bw.Flush(); err != nil { - return - } - - // Step 28-29, 32-40. read response from server. - resp, err := http.ReadResponse(br, &http.Request{Method: "GET"}) - if err != nil { - return err - } - // Step 30. check response code is 101. - if resp.StatusCode != 101 { - return ErrBadStatus - } - - // Step 41. check websocket headers. - if resp.Header.Get("Upgrade") != "WebSocket" || - strings.ToLower(resp.Header.Get("Connection")) != "upgrade" { - return ErrBadUpgrade - } - - if resp.Header.Get("Sec-Websocket-Origin") != config.Origin.String() { - return ErrBadWebSocketOrigin - } - - if resp.Header.Get("Sec-Websocket-Location") != config.Location.String() { - return ErrBadWebSocketLocation - } - - if len(config.Protocol) > 0 && resp.Header.Get("Sec-Websocket-Protocol") != config.Protocol[0] { - return ErrBadWebSocketProtocol - } - - // Step 42-43. get expected data from challenge data. - expected, err := getChallengeResponse(number1, number2, key3) - if err != nil { - return err - } - - // Step 44. read 16 bytes from server. - reply := make([]byte, 16) - if _, err = io.ReadFull(br, reply); err != nil { - return err - } - - // Step 45. check the reply equals to expected data. - if !bytes.Equal(expected, reply) { - return ErrChallengeResponse - } - // WebSocket connection is established. - return -} - -// Client Handshake described in (soon obsolete) -// draft-hixie-thewebsocket-protocol-75. -func hixie75ClientHandshake(config *Config, br *bufio.Reader, bw *bufio.Writer) (err error) { - if config.Version != ProtocolVersionHixie75 { - panic("wrong protocol version.") - } - bw.WriteString("GET " + config.Location.RequestURI() + " HTTP/1.1\r\n") - bw.WriteString("Upgrade: WebSocket\r\n") - bw.WriteString("Connection: Upgrade\r\n") - bw.WriteString("Host: " + config.Location.Host + "\r\n") - bw.WriteString("Origin: " + config.Origin.String() + "\r\n") - if len(config.Protocol) > 0 { - if len(config.Protocol) != 1 { - return ErrBadWebSocketProtocol - } - bw.WriteString("WebSocket-Protocol: " + config.Protocol[0] + "\r\n") - } - bw.WriteString("\r\n") - bw.Flush() - resp, err := http.ReadResponse(br, &http.Request{Method: "GET"}) - if err != nil { - return - } - if resp.Status != "101 Web Socket Protocol Handshake" { - return ErrBadStatus - } - if resp.Header.Get("Upgrade") != "WebSocket" || - resp.Header.Get("Connection") != "Upgrade" { - return ErrBadUpgrade - } - if resp.Header.Get("Websocket-Origin") != config.Origin.String() { - return ErrBadWebSocketOrigin - } - if resp.Header.Get("Websocket-Location") != config.Location.String() { - return ErrBadWebSocketLocation - } - if len(config.Protocol) > 0 && resp.Header.Get("Websocket-Protocol") != config.Protocol[0] { - return ErrBadWebSocketProtocol - } - return -} - -// newHixieClientConn returns new WebSocket connection speaking hixie draft protocol. -func newHixieClientConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser) *Conn { - return newHixieConn(config, buf, rwc, nil) -} - -// Gets key number from Sec-WebSocket-Key: field as described -// in 5.2 Sending the server's opening handshake, 4. -func getKeyNumber(s string) (r uint32) { - // 4. Let /key-number_n/ be the digits (characters in the range - // U+0030 DIGIT ZERO (0) to U+0039 DIGIT NINE (9)) in /key_1/, - // interpreted as a base ten integer, ignoring all other characters - // in /key_n/. - r = 0 - for i := 0; i < len(s); i++ { - if s[i] >= '0' && s[i] <= '9' { - r = r*10 + uint32(s[i]) - '0' - } - } - return -} - -// A Hixie76ServerHandshaker performs a server handshake using -// hixie draft 76 protocol. -type hixie76ServerHandshaker struct { - *Config - challengeResponse []byte -} - -func (c *hixie76ServerHandshaker) ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err error) { - c.Version = ProtocolVersionHybi00 - if req.Method != "GET" { - return http.StatusMethodNotAllowed, ErrBadRequestMethod - } - // HTTP version can be safely ignored. - - if strings.ToLower(req.Header.Get("Upgrade")) != "websocket" || - strings.ToLower(req.Header.Get("Connection")) != "upgrade" { - return http.StatusBadRequest, ErrNotWebSocket - } - - // TODO(ukai): check Host - c.Origin, err = url.ParseRequestURI(req.Header.Get("Origin")) - if err != nil { - return http.StatusBadRequest, err - } - - key1 := req.Header.Get("Sec-Websocket-Key1") - if key1 == "" { - return http.StatusBadRequest, ErrChallengeResponse - } - key2 := req.Header.Get("Sec-Websocket-Key2") - if key2 == "" { - return http.StatusBadRequest, ErrChallengeResponse - } - key3 := make([]byte, 8) - if _, err := io.ReadFull(buf, key3); err != nil { - return http.StatusBadRequest, ErrChallengeResponse - } - - var scheme string - if req.TLS != nil { - scheme = "wss" - } else { - scheme = "ws" - } - c.Location, err = url.ParseRequestURI(scheme + "://" + req.Host + req.URL.RequestURI()) - if err != nil { - return http.StatusBadRequest, err - } - - // Step 4. get key number in Sec-WebSocket-Key fields. - keyNumber1 := getKeyNumber(key1) - keyNumber2 := getKeyNumber(key2) - - // Step 5. get number of spaces in Sec-WebSocket-Key fields. - space1 := uint32(strings.Count(key1, " ")) - space2 := uint32(strings.Count(key2, " ")) - if space1 == 0 || space2 == 0 { - return http.StatusBadRequest, ErrChallengeResponse - } - - // Step 6. key number must be an integral multiple of spaces. - if keyNumber1%space1 != 0 || keyNumber2%space2 != 0 { - return http.StatusBadRequest, ErrChallengeResponse - } - - // Step 7. let part be key number divided by spaces. - part1 := keyNumber1 / space1 - part2 := keyNumber2 / space2 - - // Step 8. let challenge be concatenation of part1, part2 and key3. - // Step 9. get MD5 fingerprint of challenge. - c.challengeResponse, err = getChallengeResponse(part1, part2, key3) - if err != nil { - return http.StatusInternalServerError, err - } - protocol := strings.TrimSpace(req.Header.Get("Sec-Websocket-Protocol")) - protocols := strings.Split(protocol, ",") - for i := 0; i < len(protocols); i++ { - c.Protocol = append(c.Protocol, strings.TrimSpace(protocols[i])) - } - - return http.StatusSwitchingProtocols, nil -} - -func (c *hixie76ServerHandshaker) AcceptHandshake(buf *bufio.Writer) (err error) { - if len(c.Protocol) > 0 { - if len(c.Protocol) != 1 { - return ErrBadWebSocketProtocol - } - } - - // Step 10. send response status line. - buf.WriteString("HTTP/1.1 101 WebSocket Protocol Handshake\r\n") - // Step 11. send response headers. - buf.WriteString("Upgrade: WebSocket\r\n") - buf.WriteString("Connection: Upgrade\r\n") - buf.WriteString("Sec-WebSocket-Origin: " + c.Origin.String() + "\r\n") - buf.WriteString("Sec-WebSocket-Location: " + c.Location.String() + "\r\n") - if len(c.Protocol) > 0 { - buf.WriteString("Sec-WebSocket-Protocol: " + c.Protocol[0] + "\r\n") - } - // Step 12. send CRLF. - buf.WriteString("\r\n") - // Step 13. send response data. - buf.Write(c.challengeResponse) - return buf.Flush() -} - -func (c *hixie76ServerHandshaker) NewServerConn(buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) (conn *Conn) { - return newHixieServerConn(c.Config, buf, rwc, request) -} - -// A hixie75ServerHandshaker performs a server handshake using -// hixie draft 75 protocol. -type hixie75ServerHandshaker struct { - *Config -} - -func (c *hixie75ServerHandshaker) ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err error) { - c.Version = ProtocolVersionHixie75 - if req.Method != "GET" || req.Proto != "HTTP/1.1" { - return http.StatusMethodNotAllowed, ErrBadRequestMethod - } - if req.Header.Get("Upgrade") != "WebSocket" { - return http.StatusBadRequest, ErrNotWebSocket - } - if req.Header.Get("Connection") != "Upgrade" { - return http.StatusBadRequest, ErrNotWebSocket - } - c.Origin, err = url.ParseRequestURI(strings.TrimSpace(req.Header.Get("Origin"))) - if err != nil { - return http.StatusBadRequest, err - } - - var scheme string - if req.TLS != nil { - scheme = "wss" - } else { - scheme = "ws" - } - c.Location, err = url.ParseRequestURI(scheme + "://" + req.Host + req.URL.RequestURI()) - if err != nil { - return http.StatusBadRequest, err - } - protocol := strings.TrimSpace(req.Header.Get("Websocket-Protocol")) - protocols := strings.Split(protocol, ",") - for i := 0; i < len(protocols); i++ { - c.Protocol = append(c.Protocol, strings.TrimSpace(protocols[i])) - } - - return http.StatusSwitchingProtocols, nil -} - -func (c *hixie75ServerHandshaker) AcceptHandshake(buf *bufio.Writer) (err error) { - if len(c.Protocol) > 0 { - if len(c.Protocol) != 1 { - return ErrBadWebSocketProtocol - } - } - - buf.WriteString("HTTP/1.1 101 Web Socket Protocol Handshake\r\n") - buf.WriteString("Upgrade: WebSocket\r\n") - buf.WriteString("Connection: Upgrade\r\n") - buf.WriteString("WebSocket-Origin: " + c.Origin.String() + "\r\n") - buf.WriteString("WebSocket-Location: " + c.Location.String() + "\r\n") - if len(c.Protocol) > 0 { - buf.WriteString("WebSocket-Protocol: " + c.Protocol[0] + "\r\n") - } - buf.WriteString("\r\n") - return buf.Flush() -} - -func (c *hixie75ServerHandshaker) NewServerConn(buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) (conn *Conn) { - return newHixieServerConn(c.Config, buf, rwc, request) -} - -// newHixieServerConn returns a new WebSocket connection speaking hixie draft protocol. -func newHixieServerConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn { - return newHixieConn(config, buf, rwc, request) -} diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/websocket/hixie_test.go juju-core-1.18.1/src/code.google.com/p/go.net/websocket/hixie_test.go --- juju-core-1.18.0/src/code.google.com/p/go.net/websocket/hixie_test.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/websocket/hixie_test.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,201 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package websocket - -import ( - "bufio" - "bytes" - "fmt" - "io" - "net/http" - "net/url" - "strings" - "testing" -) - -// Test the getChallengeResponse function with values from section -// 5.1 of the specification steps 18, 26, and 43 from -// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00 -func TestHixie76Challenge(t *testing.T) { - var part1 uint32 = 777007543 - var part2 uint32 = 114997259 - key3 := []byte{0x47, 0x30, 0x22, 0x2D, 0x5A, 0x3F, 0x47, 0x58} - expected := []byte("0st3Rl&q-2ZU^weu") - - response, err := getChallengeResponse(part1, part2, key3) - if err != nil { - t.Errorf("getChallengeResponse: returned error %v", err) - return - } - if !bytes.Equal(expected, response) { - t.Errorf("getChallengeResponse: expected %q got %q", expected, response) - } -} - -func TestHixie76ClientHandshake(t *testing.T) { - b := bytes.NewBuffer([]byte{}) - bw := bufio.NewWriter(b) - br := bufio.NewReader(strings.NewReader(`HTTP/1.1 101 WebSocket Protocol Handshake -Upgrade: WebSocket -Connection: Upgrade -Sec-WebSocket-Origin: http://example.com -Sec-WebSocket-Location: ws://example.com/demo -Sec-WebSocket-Protocol: sample - -8jKS'y:G*Co,Wxa-`)) - - var err error - config := new(Config) - config.Location, err = url.ParseRequestURI("ws://example.com/demo") - if err != nil { - t.Fatal("location url", err) - } - config.Origin, err = url.ParseRequestURI("http://example.com") - if err != nil { - t.Fatal("origin url", err) - } - config.Protocol = append(config.Protocol, "sample") - config.Version = ProtocolVersionHixie76 - - config.handshakeData = map[string]string{ - "key1": "4 @1 46546xW%0l 1 5", - "number1": "829309203", - "key2": "12998 5 Y3 1 .P00", - "number2": "259970620", - "key3": "^n:ds[4U", - } - err = hixie76ClientHandshake(config, br, bw) - if err != nil { - t.Errorf("handshake failed: %v", err) - } - req, err := http.ReadRequest(bufio.NewReader(b)) - if err != nil { - t.Fatalf("read request: %v", err) - } - if req.Method != "GET" { - t.Errorf("request method expected GET, but got %q", req.Method) - } - if req.URL.Path != "/demo" { - t.Errorf("request path expected /demo, but got %q", req.URL.Path) - } - if req.Proto != "HTTP/1.1" { - t.Errorf("request proto expected HTTP/1.1, but got %q", req.Proto) - } - if req.Host != "example.com" { - t.Errorf("request Host expected example.com, but got %v", req.Host) - } - var expectedHeader = map[string]string{ - "Connection": "Upgrade", - "Upgrade": "WebSocket", - "Origin": "http://example.com", - "Sec-Websocket-Key1": config.handshakeData["key1"], - "Sec-Websocket-Key2": config.handshakeData["key2"], - "Sec-WebSocket-Protocol": config.Protocol[0], - } - for k, v := range expectedHeader { - if req.Header.Get(k) != v { - t.Errorf(fmt.Sprintf("%s expected %q but got %q", k, v, req.Header.Get(k))) - } - } -} - -func TestHixie76ServerHandshake(t *testing.T) { - config := new(Config) - handshaker := &hixie76ServerHandshaker{Config: config} - br := bufio.NewReader(strings.NewReader(`GET /demo HTTP/1.1 -Host: example.com -Connection: Upgrade -Sec-WebSocket-Key2: 12998 5 Y3 1 .P00 -Sec-WebSocket-Protocol: sample -Upgrade: WebSocket -Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5 -Origin: http://example.com - -^n:ds[4U`)) - req, err := http.ReadRequest(br) - if err != nil { - t.Fatal("request", err) - } - code, err := handshaker.ReadHandshake(br, req) - if err != nil { - t.Errorf("handshake failed: %v", err) - } - if code != http.StatusSwitchingProtocols { - t.Errorf("status expected %q but got %q", http.StatusSwitchingProtocols, code) - } - b := bytes.NewBuffer([]byte{}) - bw := bufio.NewWriter(b) - - err = handshaker.AcceptHandshake(bw) - if err != nil { - t.Errorf("handshake response failed: %v", err) - } - expectedResponse := strings.Join([]string{ - "HTTP/1.1 101 WebSocket Protocol Handshake", - "Upgrade: WebSocket", - "Connection: Upgrade", - "Sec-WebSocket-Origin: http://example.com", - "Sec-WebSocket-Location: ws://example.com/demo", - "Sec-WebSocket-Protocol: sample", - "", ""}, "\r\n") + "8jKS'y:G*Co,Wxa-" - if b.String() != expectedResponse { - t.Errorf("handshake expected %q but got %q", expectedResponse, b.String()) - } -} - -func TestHixie76SkipLengthFrame(t *testing.T) { - b := []byte{'\x80', '\x01', 'x', 0, 'h', 'e', 'l', 'l', 'o', '\xff'} - buf := bytes.NewBuffer(b) - br := bufio.NewReader(buf) - bw := bufio.NewWriter(buf) - config := newConfig(t, "/") - ws := newHixieConn(config, bufio.NewReadWriter(br, bw), nil, nil) - msg := make([]byte, 5) - n, err := ws.Read(msg) - if err != nil { - t.Errorf("Read: %v", err) - } - if !bytes.Equal(b[4:9], msg[0:n]) { - t.Errorf("Read: expected %q got %q", b[4:9], msg[0:n]) - } -} - -func TestHixie76SkipNoUTF8Frame(t *testing.T) { - b := []byte{'\x01', 'n', '\xff', 0, 'h', 'e', 'l', 'l', 'o', '\xff'} - buf := bytes.NewBuffer(b) - br := bufio.NewReader(buf) - bw := bufio.NewWriter(buf) - config := newConfig(t, "/") - ws := newHixieConn(config, bufio.NewReadWriter(br, bw), nil, nil) - msg := make([]byte, 5) - n, err := ws.Read(msg) - if err != nil { - t.Errorf("Read: %v", err) - } - if !bytes.Equal(b[4:9], msg[0:n]) { - t.Errorf("Read: expected %q got %q", b[4:9], msg[0:n]) - } -} - -func TestHixie76ClosingFrame(t *testing.T) { - b := []byte{0, 'h', 'e', 'l', 'l', 'o', '\xff'} - buf := bytes.NewBuffer(b) - br := bufio.NewReader(buf) - bw := bufio.NewWriter(buf) - config := newConfig(t, "/") - ws := newHixieConn(config, bufio.NewReadWriter(br, bw), nil, nil) - msg := make([]byte, 5) - n, err := ws.Read(msg) - if err != nil { - t.Errorf("read: %v", err) - } - if !bytes.Equal(b[1:6], msg[0:n]) { - t.Errorf("Read: expected %q got %q", b[1:6], msg[0:n]) - } - n, err = ws.Read(msg) - if err != io.EOF { - t.Errorf("read: %v", err) - } -} diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/websocket/hybi.go juju-core-1.18.1/src/code.google.com/p/go.net/websocket/hybi.go --- juju-core-1.18.0/src/code.google.com/p/go.net/websocket/hybi.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/websocket/hybi.go 2014-04-11 09:53:46.000000000 +0000 @@ -385,21 +385,8 @@ return } -func isHybiVersion(version int) bool { - switch version { - case ProtocolVersionHybi08, ProtocolVersionHybi13: - return true - default: - } - return false -} - // Client handshake described in draft-ietf-hybi-thewebsocket-protocol-17 func hybiClientHandshake(config *Config, br *bufio.Reader, bw *bufio.Writer) (err error) { - if !isHybiVersion(config.Version) { - panic("wrong protocol version.") - } - bw.WriteString("GET " + config.Location.RequestURI() + " HTTP/1.1\r\n") bw.WriteString("Host: " + config.Location.Host + "\r\n") @@ -410,11 +397,12 @@ nonce = []byte(config.handshakeData["key"]) } bw.WriteString("Sec-WebSocket-Key: " + string(nonce) + "\r\n") - if config.Version == ProtocolVersionHybi13 { - bw.WriteString("Origin: " + strings.ToLower(config.Origin.String()) + "\r\n") - } else if config.Version == ProtocolVersionHybi08 { - bw.WriteString("Sec-WebSocket-Origin: " + strings.ToLower(config.Origin.String()) + "\r\n") + bw.WriteString("Origin: " + strings.ToLower(config.Origin.String()) + "\r\n") + + if config.Version != ProtocolVersionHybi13 { + return ErrBadProtocolVersion } + bw.WriteString("Sec-WebSocket-Version: " + fmt.Sprintf("%d", config.Version) + "\r\n") if len(config.Protocol) > 0 { bw.WriteString("Sec-WebSocket-Protocol: " + strings.Join(config.Protocol, ", ") + "\r\n") @@ -500,8 +488,6 @@ switch version { case "13": c.Version = ProtocolVersionHybi13 - case "8": - c.Version = ProtocolVersionHybi08 default: return http.StatusBadRequest, ErrBadWebSocketVersion } @@ -536,8 +522,6 @@ switch config.Version { case ProtocolVersionHybi13: origin = req.Header.Get("Origin") - case ProtocolVersionHybi08: - origin = req.Header.Get("Sec-Websocket-Origin") } if origin == "null" { return nil, nil diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/websocket/hybi_test.go juju-core-1.18.1/src/code.google.com/p/go.net/websocket/hybi_test.go --- juju-core-1.18.0/src/code.google.com/p/go.net/websocket/hybi_test.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/websocket/hybi_test.go 2014-04-11 09:53:46.000000000 +0000 @@ -157,68 +157,6 @@ } } -func TestHybiClientHandshakeHybi08(t *testing.T) { - b := bytes.NewBuffer([]byte{}) - bw := bufio.NewWriter(b) - br := bufio.NewReader(strings.NewReader(`HTTP/1.1 101 Switching Protocols -Upgrade: websocket -Connection: Upgrade -Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= -Sec-WebSocket-Protocol: chat - -`)) - var err error - config := new(Config) - config.Location, err = url.ParseRequestURI("ws://server.example.com/chat") - if err != nil { - t.Fatal("location url", err) - } - config.Origin, err = url.ParseRequestURI("http://example.com") - if err != nil { - t.Fatal("origin url", err) - } - config.Protocol = append(config.Protocol, "chat") - config.Protocol = append(config.Protocol, "superchat") - config.Version = ProtocolVersionHybi08 - - config.handshakeData = map[string]string{ - "key": "dGhlIHNhbXBsZSBub25jZQ==", - } - err = hybiClientHandshake(config, br, bw) - if err != nil { - t.Errorf("handshake failed: %v", err) - } - req, err := http.ReadRequest(bufio.NewReader(b)) - if err != nil { - t.Fatalf("read request: %v", err) - } - if req.Method != "GET" { - t.Errorf("request method expected GET, but got %q", req.Method) - } - if req.URL.Path != "/chat" { - t.Errorf("request path expected /demo, but got %q", req.URL.Path) - } - if req.Proto != "HTTP/1.1" { - t.Errorf("request proto expected HTTP/1.1, but got %q", req.Proto) - } - if req.Host != "server.example.com" { - t.Errorf("request Host expected example.com, but got %v", req.Host) - } - var expectedHeader = map[string]string{ - "Connection": "Upgrade", - "Upgrade": "websocket", - "Sec-Websocket-Key": config.handshakeData["key"], - "Sec-Websocket-Origin": config.Origin.String(), - "Sec-Websocket-Protocol": "chat, superchat", - "Sec-Websocket-Version": fmt.Sprintf("%d", ProtocolVersionHybi08), - } - for k, v := range expectedHeader { - if req.Header.Get(k) != v { - t.Errorf(fmt.Sprintf("%s expected %q but got %q", k, v, req.Header.Get(k))) - } - } -} - func TestHybiServerHandshake(t *testing.T) { config := new(Config) handshaker := &hybiServerHandshaker{Config: config} @@ -310,52 +248,6 @@ "", ""}, "\r\n") if b.String() != expectedResponse { - t.Errorf("handshake expected %q but got %q", expectedResponse, b.String()) - } -} - -func TestHybiServerHandshakeHybi08(t *testing.T) { - config := new(Config) - handshaker := &hybiServerHandshaker{Config: config} - br := bufio.NewReader(strings.NewReader(`GET /chat HTTP/1.1 -Host: server.example.com -Upgrade: websocket -Connection: Upgrade -Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== -Sec-WebSocket-Origin: http://example.com -Sec-WebSocket-Protocol: chat, superchat -Sec-WebSocket-Version: 8 - -`)) - req, err := http.ReadRequest(br) - if err != nil { - t.Fatal("request", err) - } - code, err := handshaker.ReadHandshake(br, req) - if err != nil { - t.Errorf("handshake failed: %v", err) - } - if code != http.StatusSwitchingProtocols { - t.Errorf("status expected %q but got %q", http.StatusSwitchingProtocols, code) - } - b := bytes.NewBuffer([]byte{}) - bw := bufio.NewWriter(b) - - config.Protocol = []string{"chat"} - - err = handshaker.AcceptHandshake(bw) - if err != nil { - t.Errorf("handshake response failed: %v", err) - } - expectedResponse := strings.Join([]string{ - "HTTP/1.1 101 Switching Protocols", - "Upgrade: websocket", - "Connection: Upgrade", - "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", - "Sec-WebSocket-Protocol: chat", - "", ""}, "\r\n") - - if b.String() != expectedResponse { t.Errorf("handshake expected %q but got %q", expectedResponse, b.String()) } } diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/websocket/server.go juju-core-1.18.1/src/code.google.com/p/go.net/websocket/server.go --- juju-core-1.18.0/src/code.google.com/p/go.net/websocket/server.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/websocket/server.go 2014-04-11 09:53:46.000000000 +0000 @@ -23,14 +23,6 @@ return } if err != nil { - hs = &hixie76ServerHandshaker{Config: config} - code, err = hs.ReadHandshake(buf.Reader, req) - } - if err != nil { - hs = &hixie75ServerHandshaker{Config: config} - code, err = hs.ReadHandshake(buf.Reader, req) - } - if err != nil { fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code)) buf.WriteString("\r\n") buf.WriteString(err.Error()) diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/websocket/websocket.go juju-core-1.18.1/src/code.google.com/p/go.net/websocket/websocket.go --- juju-core-1.18.0/src/code.google.com/p/go.net/websocket/websocket.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/websocket/websocket.go 2014-04-11 09:53:46.000000000 +0000 @@ -21,13 +21,9 @@ ) const ( - ProtocolVersionHixie75 = -75 - ProtocolVersionHixie76 = -76 - ProtocolVersionHybi00 = 0 - ProtocolVersionHybi08 = 8 ProtocolVersionHybi13 = 13 ProtocolVersionHybi = ProtocolVersionHybi13 - SupportedProtocolVersion = "13, 8" + SupportedProtocolVersion = "13" ContinuationFrame = 0 TextFrame = 1 diff -Nru juju-core-1.18.0/src/code.google.com/p/go.net/websocket/websocket_test.go juju-core-1.18.1/src/code.google.com/p/go.net/websocket/websocket_test.go --- juju-core-1.18.0/src/code.google.com/p/go.net/websocket/websocket_test.go 2014-04-04 16:57:45.000000000 +0000 +++ juju-core-1.18.1/src/code.google.com/p/go.net/websocket/websocket_test.go 2014-04-11 09:53:46.000000000 +0000 @@ -245,7 +245,7 @@ func TestWithBadProtocol(t *testing.T) { _, err := testWithProtocol(t, []string{"test"}) if err != ErrBadStatus { - t.Errorf("SubProto: expected %q, got %q", ErrBadStatus) + t.Errorf("SubProto: expected %v, got %v", ErrBadStatus, err) } } @@ -286,6 +286,20 @@ } } +func TestDialConfigBadVersion(t *testing.T) { + once.Do(startServer) + config := newConfig(t, "/echo") + config.Version = 1234 + + _, err := DialConfig(config) + + if dialerr, ok := err.(*DialError); ok { + if dialerr.Err != ErrBadProtocolVersion { + t.Errorf("dial expected err %q but got %q", ErrBadProtocolVersion, dialerr.Err) + } + } +} + func TestSmallBuffer(t *testing.T) { // http://code.google.com/p/go/issues/detail?id=1145 // Read should be able to handle reading a fragment of a frame. diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/cmd/juju/common.go juju-core-1.18.1/src/launchpad.net/juju-core/cmd/juju/common.go --- juju-core-1.18.0/src/launchpad.net/juju-core/cmd/juju/common.go 2014-04-04 16:53:35.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/cmd/juju/common.go 2014-04-11 09:55:10.000000000 +0000 @@ -4,6 +4,8 @@ package main import ( + "fmt" + "launchpad.net/juju-core/charm" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/environs" @@ -65,6 +67,12 @@ } // Otherwise, look up the best supported series for this charm if series == "" { + if ref.Schema == "local" { + possibleUrl := &charm.URL{Reference: ref, Series: "precise"} + logger.Errorf(`The series is not specified in the environment (default-series) or with the charm. Did you mean: + %s`, possibleUrl.String()) + return nil, fmt.Errorf("cannot resolve series for charm: %q", ref) + } return client.ResolveCharm(ref) } return &charm.URL{Reference: ref, Series: series}, nil diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/cmd/juju/deploy.go juju-core-1.18.1/src/launchpad.net/juju-core/cmd/juju/deploy.go --- juju-core-1.18.0/src/launchpad.net/juju-core/cmd/juju/deploy.go 2014-04-04 16:57:31.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/cmd/juju/deploy.go 2014-04-11 09:55:10.000000000 +0000 @@ -34,8 +34,7 @@ const deployDoc = ` can be a charm URL, or an unambiguously condensed form of it; -assuming a current default series of "precise", the following forms will be -accepted. +assuming a current series of "precise", the following forms will be accepted: For cs:precise/mysql mysql @@ -44,12 +43,16 @@ For cs:~user/precise/mysql cs:~user/mysql -For local:precise/mysql - local:mysql +The current series is determined first by the default-series environment +setting, followed by the preferred series for the charm in the charm store. -In all cases, a versioned charm URL will be expanded as expected (for example, +In these cases, a versioned charm URL will be expanded as expected (for example, mysql-33 becomes cs:precise/mysql-33). +However, for local charms, when the default-series is not specified in the +environment, one must specify the series. For example: + local:precise/mysql + , if omitted, will be derived from . Constraints can be specified when using deploy by specifying the --constraints diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/cmd/juju/scp.go juju-core-1.18.1/src/launchpad.net/juju-core/cmd/juju/scp.go --- juju-core-1.18.0/src/launchpad.net/juju-core/cmd/juju/scp.go 2014-04-04 16:57:31.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/cmd/juju/scp.go 2014-04-11 09:55:10.000000000 +0000 @@ -35,12 +35,12 @@ Copy 2 files from two units to the local backup/ directory, passing -v to scp as an extra argument: - juju scp ubuntu/0:/path/file1 ubuntu/1:/path/file2 backup/ -v + juju scp -v ubuntu/0:/path/file1 ubuntu/1:/path/file2 backup/ Recursively copy the directory /var/log/mongodb/ on the first mongodb server to the local directory remote-logs: - juju scp mongodb/0:/var/log/mongodb/ remote-logs/ -r + juju scp -r mongodb/0:/var/log/mongodb/ remote-logs/ Copy a local file to the second apache unit of the environment "testing": @@ -64,6 +64,35 @@ return nil } +// expandArgs takes a list of arguments and looks for ones in the form of +// 0:some/path or service/0:some/path, and translates them into +// ubuntu@machine:some/path so they can be passed as arguments to scp, and pass +// the rest verbatim on to scp +func expandArgs(args []string, hostFromTarget func(string) (string, error)) ([]string, error) { + outArgs := make([]string, len(args)) + for i, arg := range args { + v := strings.SplitN(arg, ":", 2) + if strings.HasPrefix(arg, "-") || len(v) <= 1 { + // Can't be an interesting target, so just pass it along + outArgs[i] = arg + continue + } + host, err := hostFromTarget(v[0]) + if err != nil { + return nil, err + } + // To ensure this works with IPv6 addresses, we need to + // wrap the host with \[..\], so the colons inside will be + // interpreted as part of the address and the last one as + // separator between host and remote path. + if strings.Contains(host, ":") { + host = fmt.Sprintf(`\[%s\]`, host) + } + outArgs[i] = "ubuntu@" + host + ":" + v[1] + } + return outArgs, nil +} + // Run resolves c.Target to a machine, or host of a unit and // forks ssh with c.Args, if provided. func (c *SCPCommand) Run(ctx *cmd.Context) error { @@ -73,38 +102,9 @@ return err } defer c.apiClient.Close() - - // Parse all arguments, translating those in the form 0:/somepath - // or service/0:/somepath into ubuntu@machine:/somepath so they - // can be given to scp as targets (source(s) and destination(s)), - // and passing any others that look like extra arguments (starting - // with "-") verbatim to scp. - var targets, extraArgs []string - for i, arg := range c.Args { - if v := strings.SplitN(arg, ":", 2); len(v) > 1 { - host, err := c.hostFromTarget(v[0]) - if err != nil { - return err - } - // To ensure this works with IPv6 addresses, we need to - // wrap the host with \[..\], so the colons inside will be - // interpreted as part of the address and the last one as - // separator between host and remote path. - if strings.Contains(host, ":") { - host = fmt.Sprintf(`\[%s\]`, host) - } - targets = append(targets, "ubuntu@"+host+":"+v[1]) - continue - } - if strings.HasPrefix(arg, "-") { - if i != len(c.Args)-1 { - return fmt.Errorf("unexpected argument %q; extra arguments must be last", arg) - } - extraArgs = append(extraArgs, arg) - } else { - // Local path - targets = append(targets, arg) - } + args, err := expandArgs(c.Args, c.hostFromTarget) + if err != nil { + return err } - return ssh.Copy(targets, extraArgs, nil) + return ssh.Copy(args, nil) } diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/cmd/juju/scp_test.go juju-core-1.18.1/src/launchpad.net/juju-core/cmd/juju/scp_test.go --- juju-core-1.18.0/src/launchpad.net/juju-core/cmd/juju/scp_test.go 2014-04-04 16:57:31.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/cmd/juju/scp_test.go 2014-04-11 09:55:10.000000000 +0000 @@ -9,7 +9,9 @@ "io/ioutil" "net/url" "path/filepath" + "strings" + jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm" @@ -18,11 +20,14 @@ ) var _ = gc.Suite(&SCPSuite{}) +var _ = gc.Suite(&expandArgsSuite{}) type SCPSuite struct { SSHCommonSuite } +type expandArgsSuite struct{} + var scpTests = []struct { about string args []string @@ -34,60 +39,61 @@ []string{"0:foo", "."}, commonArgs + "ubuntu@dummyenv-0.dns:foo .\n", "", - }, - { + }, { "scp from machine 0 to current dir with extra args", - []string{"0:foo", ".", "-rv -o SomeOption"}, - commonArgs + "-rv -o SomeOption ubuntu@dummyenv-0.dns:foo .\n", + []string{"0:foo", ".", "-rv", "-o", "SomeOption"}, + commonArgs + "ubuntu@dummyenv-0.dns:foo . -rv -o SomeOption\n", "", - }, - { + }, { "scp from current dir to machine 0", []string{"foo", "0:"}, commonArgs + "foo ubuntu@dummyenv-0.dns:\n", "", - }, - { + }, { "scp from current dir to machine 0 with extra args", - []string{"foo", "0:", "-r -v"}, - commonArgs + "-r -v foo ubuntu@dummyenv-0.dns:\n", + []string{"foo", "0:", "-r", "-v"}, + commonArgs + "foo ubuntu@dummyenv-0.dns: -r -v\n", "", - }, - { + }, { "scp from machine 0 to unit mysql/0", []string{"0:foo", "mysql/0:/foo"}, commonArgs + "ubuntu@dummyenv-0.dns:foo ubuntu@dummyenv-0.dns:/foo\n", "", - }, - { + }, { "scp from machine 0 to unit mysql/0 and extra args", []string{"0:foo", "mysql/0:/foo", "-q"}, - commonArgs + "-q ubuntu@dummyenv-0.dns:foo ubuntu@dummyenv-0.dns:/foo\n", + commonArgs + "ubuntu@dummyenv-0.dns:foo ubuntu@dummyenv-0.dns:/foo -q\n", "", - }, - { + }, { "scp from machine 0 to unit mysql/0 and extra args before", []string{"-q", "-r", "0:foo", "mysql/0:/foo"}, + commonArgs + "-q -r ubuntu@dummyenv-0.dns:foo ubuntu@dummyenv-0.dns:/foo\n", "", - `unexpected argument "-q"; extra arguments must be last`, - }, - { + }, { "scp two local files to unit mysql/0", []string{"file1", "file2", "mysql/0:/foo/"}, commonArgs + "file1 file2 ubuntu@dummyenv-0.dns:/foo/\n", "", - }, - { + }, { "scp from unit mongodb/1 to unit mongodb/0 and multiple extra args", - []string{"mongodb/1:foo", "mongodb/0:", "-r -v -q -l5"}, - commonArgs + "-r -v -q -l5 ubuntu@dummyenv-2.dns:foo ubuntu@dummyenv-1.dns:\n", + []string{"mongodb/1:foo", "mongodb/0:", "-r", "-v", "-q", "-l5"}, + commonArgs + "ubuntu@dummyenv-2.dns:foo ubuntu@dummyenv-1.dns: -r -v -q -l5\n", "", - }, - { + }, { + "scp from unit mongodb/1 to unit mongodb/0 with a --", + []string{"--", "-r", "-v", "mongodb/1:foo", "mongodb/0:", "-q", "-l5"}, + commonArgs + "-- -r -v ubuntu@dummyenv-2.dns:foo ubuntu@dummyenv-1.dns: -q -l5\n", + "", + }, { "scp works with IPv6 addresses", []string{"ipv6-svc/0:foo", "bar"}, commonArgs + `ubuntu@\[2001:db8::\]:foo bar` + "\n", "", + }, { + "scp with no such machine", + []string{"5:foo", "bar"}, + "", + "machine 5 not found", }, } @@ -140,3 +146,70 @@ } } } + +var hostsFromTargets = map[string]string{ + "0": "dummyenv-0.dns", + "mysql/0": "dummyenv-0.dns", + "mongodb/0": "dummyenv-1.dns", + "mongodb/1": "dummyenv-2.dns", + "ipv6-svc/0": "2001:db8::", +} + +func dummyHostsFromTarget(target string) (string, error) { + if res, ok := hostsFromTargets[target]; ok { + return res, nil + } + return target, nil +} + +func (s *expandArgsSuite) TestSCPExpandArgs(c *gc.C) { + for i, t := range scpTests { + if t.error != "" { + // We are just running a focused set of tests on + // expandArgs, we aren't implementing the full + // hostsFromTargets to actually trigger errors + continue + } + c.Logf("test %d: %s -> %s\n", i, t.about, t.args) + // expandArgs doesn't add the commonArgs prefix, so strip it + // off, along with the trailing '\n' + c.Check(strings.HasPrefix(t.result, commonArgs), jc.IsTrue) + argString := t.result[len(commonArgs):] + c.Check(strings.HasSuffix(argString, "\n"), jc.IsTrue) + argString = argString[:len(argString)-1] + args := strings.Split(argString, " ") + expanded, err := expandArgs(t.args, dummyHostsFromTarget) + c.Check(err, gc.IsNil) + c.Check(expanded, gc.DeepEquals, args) + } +} + +var expandTests = []struct { + about string + args []string + result []string +}{ + { + "don't expand params that start with '-'", + []string{"-0:stuff", "0:foo", "."}, + []string{"-0:stuff", "ubuntu@dummyenv-0.dns:foo", "."}, + }, +} + +func (s *expandArgsSuite) TestExpandArgs(c *gc.C) { + for i, t := range expandTests { + c.Logf("test %d: %s -> %s\n", i, t.about, t.args) + expanded, err := expandArgs(t.args, dummyHostsFromTarget) + c.Check(err, gc.IsNil) + c.Check(expanded, gc.DeepEquals, t.result) + } +} + +func (s *expandArgsSuite) TestExpandArgsPropagatesErrors(c *gc.C) { + erroringHostFromTargets := func(string) (string, error) { + return "", fmt.Errorf("this is my error") + } + expanded, err := expandArgs([]string{"foo:1", "bar"}, erroringHostFromTargets) + c.Assert(err, gc.ErrorMatches, "this is my error") + c.Check(expanded, gc.IsNil) +} diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/cmd/juju/status_test.go juju-core-1.18.1/src/launchpad.net/juju-core/cmd/juju/status_test.go --- juju-core-1.18.0/src/launchpad.net/juju-core/cmd/juju/status_test.go 2014-04-04 16:57:31.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/cmd/juju/status_test.go 2014-04-11 09:55:10.000000000 +0000 @@ -28,6 +28,12 @@ "launchpad.net/juju-core/version" ) +func newPublicAddress(s string) instance.Address { + addr := instance.NewAddress(s) + addr.NetworkScope = instance.NetworkPublic + return addr +} + func runStatus(c *gc.C, args ...string) (code int, stdout, stderr []byte) { ctx := coretesting.Context(c) code = cmd.Main(&StatusCommand{}, ctx, args) @@ -250,7 +256,7 @@ startAliveMachine{"0"}, setAddresses{"0", []instance.Address{ instance.NewAddress("10.0.0.1"), - instance.NewAddress("dummyenv-0.dns"), + newPublicAddress("dummyenv-0.dns"), }}, expect{ "simulate the PA starting an instance in response to the state change", @@ -306,7 +312,7 @@ setMachineStatus{"0", params.StatusStarted, ""}, setAddresses{"0", []instance.Address{ instance.NewAddress("10.0.0.1"), - instance.NewAddress("dummyenv-0.dns"), + newPublicAddress("dummyenv-0.dns"), }}, addCharm{"dummy"}, addService{ @@ -352,7 +358,7 @@ addMachine{machineId: "0", cons: machineCons, job: state.JobManageEnviron}, setAddresses{"0", []instance.Address{ instance.NewAddress("10.0.0.1"), - instance.NewAddress("dummyenv-0.dns"), + newPublicAddress("dummyenv-0.dns"), }}, startAliveMachine{"0"}, setMachineStatus{"0", params.StatusStarted, ""}, diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/cmd/plugins/juju-restore/restore.go juju-core-1.18.1/src/launchpad.net/juju-core/cmd/plugins/juju-restore/restore.go --- juju-core-1.18.0/src/launchpad.net/juju-core/cmd/plugins/juju-restore/restore.go 2014-04-04 16:57:31.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/cmd/plugins/juju-restore/restore.go 2014-04-11 09:55:10.000000000 +0000 @@ -434,7 +434,7 @@ } func sendViaScp(file, host, destFile string) error { - err := ssh.Copy([]string{file, "ubuntu@" + host + ":" + destFile}, nil, nil) + err := ssh.Copy([]string{file, "ubuntu@" + host + ":" + destFile}, nil) if err != nil { return fmt.Errorf("scp command failed: %v", err) } diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/CONTRIBUTING juju-core-1.18.1/src/launchpad.net/juju-core/CONTRIBUTING --- juju-core-1.18.0/src/launchpad.net/juju-core/CONTRIBUTING 2014-04-04 16:53:35.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/CONTRIBUTING 2014-04-11 09:53:38.000000000 +0000 @@ -132,6 +132,19 @@ go test launchpad.net/juju-core +MongoDB +------- + +Many tests use a standalone instance of mongod as part of their setup. The +`mongod` binary found in $PATH is executed by these suites. + +Some tests (particularly those under ./store/...) assume a MongoDB instance +that supports Javascript for map-reduce functions. These functions are not +supported by juju-mongodb and the associated tests will fail unless disabled +with an environment variable: + + JUJU_NOTEST_MONGOJS=1 go test launchpad.net/juju-core/... + Proposing ========= diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/dependencies.tsv juju-core-1.18.1/src/launchpad.net/juju-core/dependencies.tsv --- juju-core-1.18.0/src/launchpad.net/juju-core/dependencies.tsv 2014-04-04 16:57:31.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/dependencies.tsv 2014-04-11 09:55:10.000000000 +0000 @@ -1,5 +1,5 @@ code.google.com/p/go.crypto hg 6478cc9340cbbe6c04511280c5007722269108e9 184 -code.google.com/p/go.net hg 3591c18acabc99439c783463ef00e6dc277eee39 77 +code.google.com/p/go.net hg c17ad62118ea511e1051721b429779fa40bddc74 116 github.com/errgo/errgo git 93d72bf813883d1054cae1c001d3a46603f7f559 github.com/joyent/gocommon git 98b151a080efe19bcde223d2d3b04389963d2347 github.com/joyent/gomanta git ff785814c0ebb4050420a2f1d47895b35b8808f2 diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/environs/manual/addresses_test.go juju-core-1.18.1/src/launchpad.net/juju-core/environs/manual/addresses_test.go --- juju-core-1.18.0/src/launchpad.net/juju-core/environs/manual/addresses_test.go 2014-04-04 16:57:31.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/environs/manual/addresses_test.go 2014-04-11 09:55:10.000000000 +0000 @@ -31,7 +31,7 @@ c.Assert(err, gc.IsNil) c.Assert(addrs, gc.HasLen, 3) // The last address is marked public, all others are unknown. - c.Assert(addrs[0].NetworkScope, gc.Equals, instance.NetworkUnknown) + c.Assert(addrs[0].NetworkScope, gc.Equals, instance.NetworkCloudLocal) c.Assert(addrs[1].NetworkScope, gc.Equals, instance.NetworkUnknown) c.Assert(addrs[2].NetworkScope, gc.Equals, instance.NetworkPublic) } diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/instance/address.go juju-core-1.18.1/src/launchpad.net/juju-core/instance/address.go --- juju-core-1.18.0/src/launchpad.net/juju-core/instance/address.go 2014-04-04 16:57:31.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/instance/address.go 2014-04-11 09:55:10.000000000 +0000 @@ -9,6 +9,22 @@ "strconv" ) +// Private network ranges for IPv4. +// See: http://tools.ietf.org/html/rfc1918 +var ( + classAPrivate = mustParseCIDR("10.0.0.0/8") + classBPrivate = mustParseCIDR("172.16.0.0/12") + classCPrivate = mustParseCIDR("192.168.0.0/16") +) + +func mustParseCIDR(s string) *net.IPNet { + _, net, err := net.ParseCIDR(s) + if err != nil { + panic(err) + } + return net +} + // AddressType represents the possible ways of specifying a machine location by // either a hostname resolvable by dns lookup, or ipv4 or ipv6 address. type AddressType string @@ -100,27 +116,69 @@ func DeriveAddressType(value string) AddressType { ip := net.ParseIP(value) - if ip != nil { - if ip.To4() != nil { - return Ipv4Address - } - if ip.To16() != nil { - return Ipv6Address - } + switch { + case ip == nil: + // TODO(gz): Check value is a valid hostname + return HostName + case ip.To4() != nil: + return Ipv4Address + case ip.To16() != nil: + return Ipv6Address + default: panic("Unknown form of IP address") } - // TODO(gz): Check value is a valid hostname - return HostName } +// NewAddress creates a new Address, deriving its type from the value. +// NewAddress will attempt derive the scope based on reserved IP address +// ranges. func NewAddress(value string) Address { - addresstype := DeriveAddressType(value) - return Address{value, addresstype, "", NetworkUnknown} + addr := Address{ + Value: value, + Type: DeriveAddressType(value), + NetworkScope: NetworkUnknown, + } + addr.NetworkScope = deriveNetworkScope(addr) + return addr } // netLookupIP is a var for testing. var netLookupIP = net.LookupIP +func isIPv4PrivateNetworkAddress(ip net.IP) bool { + return classAPrivate.Contains(ip) || + classBPrivate.Contains(ip) || + classCPrivate.Contains(ip) +} + +// deriveNetworkScope attempts to derive the network scope from an address's +// type and value, returning the original network scope if no deduction can +// be made. +func deriveNetworkScope(addr Address) NetworkScope { + if addr.Type == HostName { + return addr.NetworkScope + } + ip := net.ParseIP(addr.Value) + if ip == nil { + return addr.NetworkScope + } + if ip.IsLoopback() { + return NetworkMachineLocal + } + switch addr.Type { + case Ipv4Address: + if isIPv4PrivateNetworkAddress(ip) { + return NetworkCloudLocal + } + // If it's not loopback, and it's not a private + // network address, then it's publicly routable. + return NetworkPublic + case Ipv6Address: + // TODO(axw) check for IPv6 unique local address, if/when we care. + } + return addr.NetworkScope +} + // HostAddresses looks up the IP addresses of the specified // host, and translates them into instance.Address values. // @@ -151,6 +209,7 @@ } addrs[i].Value = ipaddr.String() } + addrs[i].NetworkScope = deriveNetworkScope(addrs[i]) } addrs[len(addrs)-1] = hostAddr return addrs, err @@ -160,9 +219,9 @@ // be appropriate to display as a publicly accessible endpoint. // If there are no suitable addresses, the empty string is returned. func SelectPublicAddress(addresses []Address) string { - index := publicAddressIndex(len(addresses), func(i int) Address { + index := bestAddressIndex(len(addresses), func(i int) Address { return addresses[i] - }) + }, publicMatch) if index < 0 { return "" } @@ -170,40 +229,22 @@ } func SelectPublicHostPort(hps []HostPort) string { - index := publicAddressIndex(len(hps), func(i int) Address { + index := bestAddressIndex(len(hps), func(i int) Address { return hps[i].Address - }) + }, publicMatch) if index < 0 { return "" } return hps[index].NetAddr() } -// publicAddressIndex is the internal version of SelectPublicAddress. -// It returns the index the selected address, or -1 if not found. -func publicAddressIndex(numAddr int, getAddr func(i int) Address) int { - mostPublicIndex := -1 - for i := 0; i < numAddr; i++ { - addr := getAddr(i) - if addr.Type != Ipv6Address { - switch addr.NetworkScope { - case NetworkPublic: - return i - case NetworkCloudLocal, NetworkUnknown: - mostPublicIndex = i - } - } - } - return mostPublicIndex -} - // SelectInternalAddress picks one address from a slice that can be // used as an endpoint for juju internal communication. // If there are no suitable addresses, the empty string is returned. func SelectInternalAddress(addresses []Address, machineLocal bool) string { - index := internalAddressIndex(len(addresses), func(i int) Address { + index := bestAddressIndex(len(addresses), func(i int) Address { return addresses[i] - }, machineLocal) + }, internalAddressMatcher(machineLocal)) if index < 0 { return "" } @@ -215,35 +256,76 @@ // and returns it in its NetAddr form. // If there are no suitable addresses, the empty string is returned. func SelectInternalHostPort(hps []HostPort, machineLocal bool) string { - index := internalAddressIndex(len(hps), func(i int) Address { + index := bestAddressIndex(len(hps), func(i int) Address { return hps[i].Address - }, machineLocal) + }, internalAddressMatcher(machineLocal)) if index < 0 { return "" } return hps[index].NetAddr() } -// internalAddressIndex is the internal version of SelectInternalAddress. -// It returns the index the selected address, or -1 if not found. -func internalAddressIndex(numAddr int, getAddr func(i int) Address, machineLocal bool) int { - usableAddressIndex := -1 +func publicMatch(addr Address) scopeMatch { + switch addr.NetworkScope { + case NetworkPublic: + return exactScope + case NetworkCloudLocal, NetworkUnknown: + return fallbackScope + } + return invalidScope +} + +func internalAddressMatcher(machineLocal bool) func(Address) scopeMatch { + if machineLocal { + return cloudOrMachineLocalMatch + } + return cloudLocalMatch +} + +func cloudLocalMatch(addr Address) scopeMatch { + switch addr.NetworkScope { + case NetworkCloudLocal: + return exactScope + case NetworkPublic, NetworkUnknown: + return fallbackScope + } + return invalidScope +} + +func cloudOrMachineLocalMatch(addr Address) scopeMatch { + if addr.NetworkScope == NetworkMachineLocal { + return exactScope + } + return cloudLocalMatch(addr) +} + +type scopeMatch int + +const ( + invalidScope scopeMatch = iota + exactScope + fallbackScope +) + +// bestAddressIndex returns the index of the first address +// with an exactly matching scope, or the first address with +// a matching fallback scope if there are no exact matches. +// If there are no suitable addresses, -1 is returned. +func bestAddressIndex(numAddr int, getAddr func(i int) Address, match func(addr Address) scopeMatch) int { + fallbackAddressIndex := -1 for i := 0; i < numAddr; i++ { addr := getAddr(i) if addr.Type != Ipv6Address { - switch addr.NetworkScope { - case NetworkCloudLocal: + switch match(addr) { + case exactScope: return i - case NetworkMachineLocal: - if machineLocal { - return i - } - case NetworkPublic, NetworkUnknown: - if usableAddressIndex == -1 { - usableAddressIndex = i + case fallbackScope: + // Use the first fallback address if there are no exact matches. + if fallbackAddressIndex == -1 { + fallbackAddressIndex = i } } } } - return usableAddressIndex + return fallbackAddressIndex } diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/instance/address_test.go juju-core-1.18.1/src/launchpad.net/juju-core/instance/address_test.go --- juju-core-1.18.0/src/launchpad.net/juju-core/instance/address_test.go 2014-04-04 16:57:31.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/instance/address_test.go 2014-04-11 09:55:10.000000000 +0000 @@ -20,15 +20,47 @@ var _ = gc.Suite(&AddressSuite{}) func (s *AddressSuite) TestNewAddressIpv4(c *gc.C) { - addr := NewAddress("127.0.0.1") - c.Check(addr.Value, gc.Equals, "127.0.0.1") - c.Check(addr.Type, gc.Equals, Ipv4Address) + type test struct { + value string + expectedScope NetworkScope + } + + tests := []test{{ + value: "127.0.0.1", + expectedScope: NetworkMachineLocal, + }, { + value: "10.0.3.1", + expectedScope: NetworkCloudLocal, + }, { + value: "172.16.15.14", + expectedScope: NetworkCloudLocal, + }, { + value: "192.168.0.1", + expectedScope: NetworkCloudLocal, + }, { + value: "8.8.8.8", + expectedScope: NetworkPublic, + }} + + for _, t := range tests { + c.Logf("test %s %s", t.value) + addr := NewAddress(t.value) + c.Check(addr.Value, gc.Equals, t.value) + c.Check(addr.Type, gc.Equals, Ipv4Address) + c.Check(addr.NetworkScope, gc.Equals, t.expectedScope) + } } func (s *AddressSuite) TestNewAddressIpv6(c *gc.C) { addr := NewAddress("::1") c.Check(addr.Value, gc.Equals, "::1") c.Check(addr.Type, gc.Equals, Ipv6Address) + c.Check(addr.NetworkScope, gc.Equals, NetworkMachineLocal) + + addr = NewAddress("2001:DB8::1") + c.Check(addr.Value, gc.Equals, "2001:DB8::1") + c.Check(addr.Type, gc.Equals, Ipv6Address) + c.Check(addr.NetworkScope, gc.Equals, NetworkUnknown) } func (s *AddressSuite) TestNewAddresses(c *gc.C) { @@ -36,8 +68,11 @@ []string{"127.0.0.1", "192.168.1.1", "192.168.178.255"}) c.Assert(len(addresses), gc.Equals, 3) c.Assert(addresses[0].Value, gc.Equals, "127.0.0.1") + c.Assert(addresses[0].NetworkScope, gc.Equals, NetworkMachineLocal) c.Assert(addresses[1].Value, gc.Equals, "192.168.1.1") + c.Assert(addresses[1].NetworkScope, gc.Equals, NetworkCloudLocal) c.Assert(addresses[2].Value, gc.Equals, "192.168.178.255") + c.Assert(addresses[2].NetworkScope, gc.Equals, NetworkCloudLocal) } func (s *AddressSuite) TestNewAddressHostname(c *gc.C) { @@ -120,12 +155,12 @@ }, 2, }, { - "last unknown address selected", + "first unknown address selected", []Address{ {"10.0.0.1", Ipv4Address, "cloud", NetworkUnknown}, {"8.8.8.8", Ipv4Address, "floating", NetworkUnknown}, }, - 1, + 0, }} func (s *AddressSuite) TestSelectPublicAddress(c *gc.C) { diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/provider/local/environprovider.go juju-core-1.18.1/src/launchpad.net/juju-core/provider/local/environprovider.go --- juju-core-1.18.0/src/launchpad.net/juju-core/provider/local/environprovider.go 2014-04-04 16:57:31.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/provider/local/environprovider.go 2014-04-11 09:55:10.000000000 +0000 @@ -256,6 +256,10 @@ # # network-bridge: lxcbr0 + # The default series to deploy the state-server and charms on. + # + # default-series: precise + `[1:] } diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/provider/maas/instance.go juju-core-1.18.1/src/launchpad.net/juju-core/provider/maas/instance.go --- juju-core-1.18.0/src/launchpad.net/juju-core/provider/maas/instance.go 2014-04-04 16:53:35.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/provider/maas/instance.go 2014-04-11 09:55:10.000000000 +0000 @@ -82,7 +82,7 @@ } for _, ip := range ips { - a := instance.Address{ip, instance.DeriveAddressType(ip), "", instance.NetworkUnknown} + a := instance.NewAddress(ip) addrs = append(addrs, a) } diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/provider/openstack/provider.go juju-core-1.18.1/src/launchpad.net/juju-core/provider/openstack/provider.go --- juju-core-1.18.0/src/launchpad.net/juju-core/provider/openstack/provider.go 2014-04-04 16:57:31.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/provider/openstack/provider.go 2014-04-11 09:55:10.000000000 +0000 @@ -408,12 +408,13 @@ if address.Version == 6 { addrtype = instance.Ipv6Address } - // TODO(gz): Use NewAddress... with sanity checking - machineAddr := instance.Address{ - Value: address.Address, - Type: addrtype, - NetworkName: network, - NetworkScope: networkscope, + machineAddr := instance.NewAddress(address.Address) + if networkscope != instance.NetworkUnknown { + machineAddr.NetworkScope = networkscope + } + machineAddr.NetworkName = network + if machineAddr.Type != addrtype { + logger.Warningf("derived address type %v, nova reports %v", machineAddr.Type, addrtype) } machineAddresses = append(machineAddresses, machineAddr) } diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/provider/openstack/provider_test.go juju-core-1.18.1/src/launchpad.net/juju-core/provider/openstack/provider_test.go --- juju-core-1.18.0/src/launchpad.net/juju-core/provider/openstack/provider_test.go 2014-04-04 16:53:35.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/provider/openstack/provider_test.go 2014-04-11 09:53:38.000000000 +0000 @@ -54,13 +54,13 @@ }, { summary: "private only", - private: []nova.IPAddress{{4, "127.0.0.4"}}, + private: []nova.IPAddress{{4, "192.168.0.1"}}, networks: []string{"private"}, - expected: "127.0.0.4", + expected: "192.168.0.1", }, { summary: "private plus (HP cloud)", - private: []nova.IPAddress{{4, "127.0.0.4"}, {4, "8.8.4.4"}}, + private: []nova.IPAddress{{4, "10.0.0.1"}, {4, "8.8.4.4"}}, networks: []string{"private"}, expected: "8.8.4.4", }, @@ -72,27 +72,27 @@ }, { summary: "public and private", - private: []nova.IPAddress{{4, "127.0.0.4"}}, + private: []nova.IPAddress{{4, "10.0.0.4"}}, public: []nova.IPAddress{{4, "8.8.4.4"}}, networks: []string{"private", "public"}, expected: "8.8.4.4", }, { summary: "public private plus", - private: []nova.IPAddress{{4, "127.0.0.4"}, {4, "8.8.4.4"}}, + private: []nova.IPAddress{{4, "127.0.0.4"}, {4, "192.168.0.1"}}, public: []nova.IPAddress{{4, "8.8.8.8"}}, networks: []string{"private", "public"}, expected: "8.8.8.8", }, { summary: "custom only", - private: []nova.IPAddress{{4, "127.0.0.2"}}, + private: []nova.IPAddress{{4, "192.168.0.1"}}, networks: []string{"special"}, - expected: "127.0.0.2", + expected: "192.168.0.1", }, { summary: "custom and public", - private: []nova.IPAddress{{4, "127.0.0.2"}}, + private: []nova.IPAddress{{4, "172.16.0.1"}}, public: []nova.IPAddress{{4, "8.8.8.8"}}, networks: []string{"special", "public"}, expected: "8.8.8.8", diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/scripts/win-installer/setup.iss juju-core-1.18.1/src/launchpad.net/juju-core/scripts/win-installer/setup.iss --- juju-core-1.18.0/src/launchpad.net/juju-core/scripts/win-installer/setup.iss 2014-04-04 16:57:31.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/scripts/win-installer/setup.iss 2014-04-11 09:55:10.000000000 +0000 @@ -2,7 +2,7 @@ ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! #define MyAppName "Juju" -#define MyAppVersion "1.18.0" +#define MyAppVersion "1.18.1" #define MyAppPublisher "Canonical, Ltd" #define MyAppURL "http://juju.ubuntu.com/" #define MyAppExeName "juju.exe" diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/state/api/agent/state.go juju-core-1.18.1/src/launchpad.net/juju-core/state/api/agent/state.go --- juju-core-1.18.0/src/launchpad.net/juju-core/state/api/agent/state.go 2014-04-04 16:57:31.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/state/api/agent/state.go 2014-04-11 09:55:10.000000000 +0000 @@ -32,7 +32,7 @@ return nil, err } if len(results.Entities) != 1 { - return nil, fmt.Errorf("expected one result, got %d", len(results.Entities)) + return nil, fmt.Errorf("expected 1 result, got %d", len(results.Entities)) } if err := results.Entities[0].Error; err != nil { return nil, err diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/state/api/common/life.go juju-core-1.18.1/src/launchpad.net/juju-core/state/api/common/life.go --- juju-core-1.18.0/src/launchpad.net/juju-core/state/api/common/life.go 2014-04-04 16:53:35.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/state/api/common/life.go 2014-04-11 09:53:38.000000000 +0000 @@ -22,7 +22,7 @@ return "", err } if len(result.Results) != 1 { - return "", fmt.Errorf("expected one result, got %d", len(result.Results)) + return "", fmt.Errorf("expected 1 result, got %d", len(result.Results)) } if err := result.Results[0].Error; err != nil { return "", err diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/state/api/deployer/machine.go juju-core-1.18.1/src/launchpad.net/juju-core/state/api/deployer/machine.go --- juju-core-1.18.0/src/launchpad.net/juju-core/state/api/deployer/machine.go 2014-04-04 16:53:35.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/state/api/deployer/machine.go 2014-04-11 09:53:38.000000000 +0000 @@ -29,7 +29,7 @@ return nil, err } if len(results.Results) != 1 { - return nil, fmt.Errorf("expected one result, got %d", len(results.Results)) + return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/state/api/firewaller/machine.go juju-core-1.18.1/src/launchpad.net/juju-core/state/api/firewaller/machine.go --- juju-core-1.18.0/src/launchpad.net/juju-core/state/api/firewaller/machine.go 2014-04-04 16:53:35.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/state/api/firewaller/machine.go 2014-04-11 09:53:38.000000000 +0000 @@ -29,7 +29,7 @@ return nil, err } if len(results.Results) != 1 { - return nil, fmt.Errorf("expected one result, got %d", len(results.Results)) + return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { @@ -51,7 +51,7 @@ return "", err } if len(results.Results) != 1 { - return "", fmt.Errorf("expected one result, got %d", len(results.Results)) + return "", fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/state/api/firewaller/service.go juju-core-1.18.1/src/launchpad.net/juju-core/state/api/firewaller/service.go --- juju-core-1.18.0/src/launchpad.net/juju-core/state/api/firewaller/service.go 2014-04-04 16:53:35.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/state/api/firewaller/service.go 2014-04-11 09:53:38.000000000 +0000 @@ -38,7 +38,7 @@ return nil, err } if len(results.Results) != 1 { - return nil, fmt.Errorf("expected one result, got %d", len(results.Results)) + return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { @@ -80,7 +80,7 @@ return false, err } if len(results.Results) != 1 { - return false, fmt.Errorf("expected one result, got %d", len(results.Results)) + return false, fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/state/api/firewaller/unit.go juju-core-1.18.1/src/launchpad.net/juju-core/state/api/firewaller/unit.go --- juju-core-1.18.0/src/launchpad.net/juju-core/state/api/firewaller/unit.go 2014-04-04 16:53:35.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/state/api/firewaller/unit.go 2014-04-11 09:53:38.000000000 +0000 @@ -54,7 +54,7 @@ return nil, err } if len(results.Results) != 1 { - return nil, fmt.Errorf("expected one result, got %d", len(results.Results)) + return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { @@ -94,7 +94,7 @@ return nil, err } if len(results.Results) != 1 { - return nil, fmt.Errorf("expected one result, got %d", len(results.Results)) + return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { @@ -115,7 +115,7 @@ return "", err } if len(results.Results) != 1 { - return "", fmt.Errorf("expected one result, got %d", len(results.Results)) + return "", fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/state/api/keyupdater/authorisedkeys.go juju-core-1.18.1/src/launchpad.net/juju-core/state/api/keyupdater/authorisedkeys.go --- juju-core-1.18.0/src/launchpad.net/juju-core/state/api/keyupdater/authorisedkeys.go 2014-04-04 16:53:35.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/state/api/keyupdater/authorisedkeys.go 2014-04-11 09:53:38.000000000 +0000 @@ -38,7 +38,7 @@ } if len(results.Results) != 1 { // TODO: Not directly tested - return nil, fmt.Errorf("expected one result, got %d", len(results.Results)) + return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if err := result.Error; err != nil { @@ -61,7 +61,7 @@ } if len(results.Results) != 1 { // TODO: Not directly tested - return nil, fmt.Errorf("expected one result, got %d", len(results.Results)) + return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/state/api/logger/logger.go juju-core-1.18.1/src/launchpad.net/juju-core/state/api/logger/logger.go --- juju-core-1.18.0/src/launchpad.net/juju-core/state/api/logger/logger.go 2014-04-04 16:53:35.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/state/api/logger/logger.go 2014-04-11 09:53:38.000000000 +0000 @@ -40,7 +40,7 @@ } if len(results.Results) != 1 { // TODO: Not directly tested - return "", fmt.Errorf("expected one result, got %d", len(results.Results)) + return "", fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if err := result.Error; err != nil { @@ -63,7 +63,7 @@ } if len(results.Results) != 1 { // TODO: Not directly tested - return nil, fmt.Errorf("expected one result, got %d", len(results.Results)) + return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/state/api/machiner/machine.go juju-core-1.18.1/src/launchpad.net/juju-core/state/api/machiner/machine.go --- juju-core-1.18.0/src/launchpad.net/juju-core/state/api/machiner/machine.go 2014-04-04 16:53:35.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/state/api/machiner/machine.go 2014-04-11 09:53:38.000000000 +0000 @@ -93,7 +93,7 @@ return nil, err } if len(results.Results) != 1 { - return nil, fmt.Errorf("expected one result, got %d", len(results.Results)) + return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/state/api/machiner/machiner_test.go juju-core-1.18.1/src/launchpad.net/juju-core/state/api/machiner/machiner_test.go --- juju-core-1.18.0/src/launchpad.net/juju-core/state/api/machiner/machiner_test.go 2014-04-04 16:57:31.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/state/api/machiner/machiner_test.go 2014-04-11 09:55:10.000000000 +0000 @@ -41,7 +41,7 @@ s.JujuConnSuite.SetUpTest(c) m, err := s.State.AddMachine("quantal", state.JobManageEnviron) c.Assert(err, gc.IsNil) - err = m.SetAddresses(instance.NewAddresses([]string{"127.0.0.1"})) + err = m.SetAddresses(instance.NewAddresses([]string{"10.0.0.1"})) c.Assert(err, gc.IsNil) s.st, s.machine = s.OpenAPIAsNewMachine(c) @@ -133,7 +133,7 @@ c.Assert(addr, gc.HasLen, 0) addresses := []instance.Address{ - instance.NewAddress("127.0.0.1"), + instance.NewAddress("10.0.0.1"), instance.NewAddress("8.8.8.8"), } err = machine.SetMachineAddresses(addresses) diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/state/api/params/params.go juju-core-1.18.1/src/launchpad.net/juju-core/state/api/params/params.go --- juju-core-1.18.0/src/launchpad.net/juju-core/state/api/params/params.go 2014-04-04 16:57:31.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/state/api/params/params.go 2014-04-11 09:55:10.000000000 +0000 @@ -50,7 +50,7 @@ // of a bulk operation on a single value. func (result ErrorResults) OneError() error { if n := len(result.Results); n != 1 { - return fmt.Errorf("expected one result, got %d", n) + return fmt.Errorf("expected 1 result, got %d", n) } if err := result.Results[0].Error; err != nil { return err diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/state/api/provisioner/machine.go juju-core-1.18.1/src/launchpad.net/juju-core/state/api/provisioner/machine.go --- juju-core-1.18.0/src/launchpad.net/juju-core/state/api/provisioner/machine.go 2014-04-04 16:57:31.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/state/api/provisioner/machine.go 2014-04-11 09:55:10.000000000 +0000 @@ -81,7 +81,7 @@ return "", "", err } if len(results.Results) != 1 { - return "", "", fmt.Errorf("expected one result, got %d", len(results.Results)) + return "", "", fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { @@ -103,7 +103,7 @@ return nothing, err } if len(results.Results) != 1 { - return nothing, fmt.Errorf("expected one result, got %d", len(results.Results)) + return nothing, fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { @@ -154,7 +154,7 @@ return "", err } if len(results.Results) != 1 { - return "", fmt.Errorf("expected one result, got %d", len(results.Results)) + return "", fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { @@ -194,7 +194,7 @@ return "", err } if len(results.Results) != 1 { - return "", fmt.Errorf("expected one result, got %d", len(results.Results)) + return "", fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { @@ -245,7 +245,7 @@ return nil, err } if len(results.Results) != 1 { - return nil, fmt.Errorf("expected one result, got %d", len(results.Results)) + return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { @@ -269,7 +269,7 @@ return nil, err } if len(results.Results) != 1 { - return nil, fmt.Errorf("expected one result, got %d", len(results.Results)) + return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { @@ -292,7 +292,7 @@ return err } if len(results.Results) != 1 { - return fmt.Errorf("expected one result, got %d", len(results.Results)) + return fmt.Errorf("expected 1 result, got %d", len(results.Results)) } apiError := results.Results[0].Error if apiError != nil { diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/state/api/provisioner/provisioner.go juju-core-1.18.1/src/launchpad.net/juju-core/state/api/provisioner/provisioner.go --- juju-core-1.18.0/src/launchpad.net/juju-core/state/api/provisioner/provisioner.go 2014-04-04 16:57:31.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/state/api/provisioner/provisioner.go 2014-04-11 09:53:38.000000000 +0000 @@ -106,7 +106,7 @@ } if len(results.Results) != 1 { // TODO: Not directly tested - return nil, fmt.Errorf("expected one result, got %d", len(results.Results)) + return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if err := result.Error; err != nil { diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/state/api/uniter/charm.go juju-core-1.18.1/src/launchpad.net/juju-core/state/api/uniter/charm.go --- juju-core-1.18.0/src/launchpad.net/juju-core/state/api/uniter/charm.go 2014-04-04 16:53:35.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/state/api/uniter/charm.go 2014-04-11 09:53:38.000000000 +0000 @@ -41,7 +41,7 @@ return "", err } if len(results.Results) != 1 { - return "", fmt.Errorf("expected one result, got %d", len(results.Results)) + return "", fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { @@ -70,7 +70,7 @@ return nil, false, err } if len(results.Results) != 1 { - return nil, false, fmt.Errorf("expected one result, got %d", len(results.Results)) + return nil, false, fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/state/api/uniter/relationunit.go juju-core-1.18.1/src/launchpad.net/juju-core/state/api/uniter/relationunit.go --- juju-core-1.18.0/src/launchpad.net/juju-core/state/api/uniter/relationunit.go 2014-04-04 16:53:35.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/state/api/uniter/relationunit.go 2014-04-11 09:53:38.000000000 +0000 @@ -117,7 +117,7 @@ return nil, err } if len(results.Results) != 1 { - return nil, fmt.Errorf("expected one result, got %d", len(results.Results)) + return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { @@ -148,7 +148,7 @@ return nil, err } if len(results.Results) != 1 { - return nil, fmt.Errorf("expected one result, got %d", len(results.Results)) + return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { @@ -172,7 +172,7 @@ return nil, err } if len(results.Results) != 1 { - return nil, fmt.Errorf("expected one result, got %d", len(results.Results)) + return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/state/api/uniter/service.go juju-core-1.18.1/src/launchpad.net/juju-core/state/api/uniter/service.go --- juju-core-1.18.0/src/launchpad.net/juju-core/state/api/uniter/service.go 2014-04-04 16:53:35.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/state/api/uniter/service.go 2014-04-11 09:53:38.000000000 +0000 @@ -47,7 +47,7 @@ return nil, err } if len(results.Results) != 1 { - return nil, fmt.Errorf("expected one result, got %d", len(results.Results)) + return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { @@ -69,7 +69,7 @@ return nil, err } if len(results.Results) != 1 { - return nil, fmt.Errorf("expected one result, got %d", len(results.Results)) + return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { @@ -111,7 +111,7 @@ return nil, false, err } if len(results.Results) != 1 { - return nil, false, fmt.Errorf("expected one result, got %d", len(results.Results)) + return nil, false, fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/state/api/uniter/uniter.go juju-core-1.18.1/src/launchpad.net/juju-core/state/api/uniter/uniter.go --- juju-core-1.18.0/src/launchpad.net/juju-core/state/api/uniter/uniter.go 2014-04-04 16:53:35.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/state/api/uniter/uniter.go 2014-04-11 09:53:38.000000000 +0000 @@ -58,7 +58,7 @@ return nothing, err } if len(result.Results) != 1 { - return nothing, fmt.Errorf("expected one result, got %d", len(result.Results)) + return nothing, fmt.Errorf("expected 1 result, got %d", len(result.Results)) } if err := result.Results[0].Error; err != nil { return nothing, err @@ -134,7 +134,7 @@ }, nil } -// Relation returns the existing relation with the given tag. +// RelationById returns the existing relation with the given id. func (st *State) RelationById(id int) (*Relation, error) { var results params.RelationResults args := params.RelationIds{ @@ -145,7 +145,7 @@ return nil, err } if len(results.Results) != 1 { - return nil, fmt.Errorf("expected one result, got %d", len(results.Results)) + return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if err := result.Error; err != nil { diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/state/api/uniter/unit.go juju-core-1.18.1/src/launchpad.net/juju-core/state/api/uniter/unit.go --- juju-core-1.18.0/src/launchpad.net/juju-core/state/api/uniter/unit.go 2014-04-04 16:53:35.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/state/api/uniter/unit.go 2014-04-11 09:55:10.000000000 +0000 @@ -94,7 +94,7 @@ return nil, err } if len(results.Results) != 1 { - return nil, fmt.Errorf("expected one result, got %d", len(results.Results)) + return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { @@ -134,7 +134,7 @@ return nil, err } if len(results.Results) != 1 { - return nil, fmt.Errorf("expected one result, got %d", len(results.Results)) + return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { @@ -197,7 +197,7 @@ return "", err } if len(results.Results) != 1 { - return "", fmt.Errorf("expected one result, got %d", len(results.Results)) + return "", fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { @@ -221,7 +221,7 @@ return false, err } if len(results.Results) != 1 { - return false, fmt.Errorf("expected one result, got %d", len(results.Results)) + return false, fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { @@ -242,7 +242,7 @@ return false, err } if len(results.Results) != 1 { - return false, fmt.Errorf("expected one result, got %d", len(results.Results)) + return false, fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { @@ -269,7 +269,7 @@ return "", err } if len(results.Results) != 1 { - return "", fmt.Errorf("expected one result, got %d", len(results.Results)) + return "", fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { @@ -314,7 +314,7 @@ return "", err } if len(results.Results) != 1 { - return "", fmt.Errorf("expected one result, got %d", len(results.Results)) + return "", fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { @@ -395,7 +395,7 @@ return nil, err } if len(results.Results) != 1 { - return nil, fmt.Errorf("expected one result, got %d", len(results.Results)) + return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { @@ -457,7 +457,7 @@ return nil, err } if len(results.Results) != 1 { - return nil, fmt.Errorf("expected one result, got %d", len(results.Results)) + return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { @@ -466,3 +466,23 @@ w := watcher.NewNotifyWatcher(u.st.caller, result) return w, nil } + +// JoinedRelations returns the tags of the relations the unit has joined. +func (u *Unit) JoinedRelations() ([]string, error) { + var results params.StringsResults + args := params.Entities{ + Entities: []params.Entity{{Tag: u.tag}}, + } + err := u.st.call("JoinedRelations", args, &results) + if err != nil { + return nil, err + } + if len(results.Results) != 1 { + return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) + } + result := results.Results[0] + if result.Error != nil { + return nil, result.Error + } + return result.Result, nil +} diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/state/api/uniter/unit_test.go juju-core-1.18.1/src/launchpad.net/juju-core/state/api/uniter/unit_test.go --- juju-core-1.18.0/src/launchpad.net/juju-core/state/api/uniter/unit_test.go 2014-04-04 16:53:35.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/state/api/uniter/unit_test.go 2014-04-11 09:55:10.000000000 +0000 @@ -4,6 +4,8 @@ package uniter_test import ( + "sort" + jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" @@ -355,3 +357,20 @@ c.Assert(s.apiUnit.ServiceName(), gc.Equals, "wordpress") c.Assert(s.apiUnit.ServiceTag(), gc.Equals, "service-wordpress") } + +func (s *unitSuite) TestJoinedRelations(c *gc.C) { + joinedRelations, err := s.apiUnit.JoinedRelations() + c.Assert(err, gc.IsNil) + c.Assert(joinedRelations, gc.HasLen, 0) + + rel1, _, _ := s.addRelatedService(c, "wordpress", "monitoring", s.wordpressUnit) + joinedRelations, err = s.apiUnit.JoinedRelations() + c.Assert(err, gc.IsNil) + c.Assert(joinedRelations, gc.DeepEquals, []string{rel1.Tag()}) + + rel2, _, _ := s.addRelatedService(c, "wordpress", "logging", s.wordpressUnit) + joinedRelations, err = s.apiUnit.JoinedRelations() + c.Assert(err, gc.IsNil) + sort.Strings(joinedRelations) + c.Assert(joinedRelations, gc.DeepEquals, []string{rel2.Tag(), rel1.Tag()}) +} diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/state/api/upgrader/upgrader.go juju-core-1.18.1/src/launchpad.net/juju-core/state/api/upgrader/upgrader.go --- juju-core-1.18.0/src/launchpad.net/juju-core/state/api/upgrader/upgrader.go 2014-04-04 16:53:35.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/state/api/upgrader/upgrader.go 2014-04-11 09:53:38.000000000 +0000 @@ -60,7 +60,7 @@ } if len(results.Results) != 1 { // TODO: Not directly tested - return version.Number{}, fmt.Errorf("expected one result, got %d", len(results.Results)) + return version.Number{}, fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if err := result.Error; err != nil { @@ -87,7 +87,7 @@ } if len(results.Results) != 1 { // TODO: Not directly tested - return nil, false, fmt.Errorf("expected one result, got %d", len(results.Results)) + return nil, false, fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if err := result.Error; err != nil { @@ -112,7 +112,7 @@ } if len(results.Results) != 1 { // TODO: Not directly tested - return nil, fmt.Errorf("expected one result, got %d", len(results.Results)) + return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/state/apiserver/machine/machiner.go juju-core-1.18.1/src/launchpad.net/juju-core/state/apiserver/machine/machiner.go --- juju-core-1.18.0/src/launchpad.net/juju-core/state/apiserver/machine/machiner.go 2014-04-04 16:53:35.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/state/apiserver/machine/machiner.go 2014-04-11 09:53:38.000000000 +0000 @@ -71,7 +71,7 @@ var m *state.Machine m, err = api.getMachine(arg.Tag) if err == nil { - err = m.SetMachineAddresses(arg.Addresses) + err = m.SetMachineAddresses(arg.Addresses...) } else if errors.IsNotFoundError(err) { err = common.ErrPerm } diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/state/apiserver/uniter/uniter.go juju-core-1.18.1/src/launchpad.net/juju-core/state/apiserver/uniter/uniter.go --- juju-core-1.18.0/src/launchpad.net/juju-core/state/apiserver/uniter/uniter.go 2014-04-04 16:53:35.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/state/apiserver/uniter/uniter.go 2014-04-11 09:55:10.000000000 +0000 @@ -739,6 +739,44 @@ return result, nil } +func joinedRelationTags(unit *state.Unit) ([]string, error) { + relations, err := unit.JoinedRelations() + if err != nil { + return nil, err + } + tags := make([]string, len(relations)) + for i, relation := range relations { + tags[i] = relation.Tag() + } + return tags, nil +} + +// JoinedRelations returns the tags of all relations each supplied unit has joined. +func (u *UniterAPI) JoinedRelations(args params.Entities) (params.StringsResults, error) { + result := params.StringsResults{ + Results: make([]params.StringsResult, len(args.Entities)), + } + if len(args.Entities) == 0 { + return result, nil + } + canRead, err := u.accessUnit() + if err != nil { + return params.StringsResults{}, err + } + for i, entity := range args.Entities { + err := common.ErrPerm + if canRead(entity.Tag) { + var unit *state.Unit + unit, err = u.getUnit(entity.Tag) + if err == nil { + result.Results[i].Result, err = joinedRelationTags(unit) + } + } + result.Results[i].Error = common.ServerError(err) + } + return result, nil +} + // CurrentEnvironUUID returns the UUID for the current juju environment. func (u *UniterAPI) CurrentEnvironUUID() (params.StringResult, error) { result := params.StringResult{} diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/state/apiserver/uniter/uniter_test.go juju-core-1.18.1/src/launchpad.net/juju-core/state/apiserver/uniter/uniter_test.go --- juju-core-1.18.0/src/launchpad.net/juju-core/state/apiserver/uniter/uniter_test.go 2014-04-04 16:57:31.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/state/apiserver/uniter/uniter_test.go 2014-04-11 09:55:10.000000000 +0000 @@ -1108,6 +1108,37 @@ c.Assert(readSettings, gc.DeepEquals, settings) } +func (s *uniterSuite) TestJoinedRelations(c *gc.C) { + rel := s.addRelation(c, "wordpress", "mysql") + relUnit, err := rel.Unit(s.wordpressUnit) + c.Assert(err, gc.IsNil) + err = relUnit.EnterScope(nil) + c.Assert(err, gc.IsNil) + + args := params.Entities{ + Entities: []params.Entity{ + {s.wordpressUnit.Tag()}, + {s.mysqlUnit.Tag()}, + {"unit-unknown-1"}, + {"service-wordpress"}, + {"machine-0"}, + {rel.Tag()}, + }, + } + result, err := s.uniter.JoinedRelations(args) + c.Assert(err, gc.IsNil) + c.Assert(result, gc.DeepEquals, params.StringsResults{ + Results: []params.StringsResult{ + {Result: []string{rel.Tag()}}, + {Error: apiservertesting.ErrUnauthorized}, + {Error: apiservertesting.ErrUnauthorized}, + {Error: apiservertesting.ErrUnauthorized}, + {Error: apiservertesting.ErrUnauthorized}, + {Error: apiservertesting.ErrUnauthorized}, + }, + }) +} + func (s *uniterSuite) TestReadSettings(c *gc.C) { rel := s.addRelation(c, "wordpress", "mysql") relUnit, err := rel.Unit(s.wordpressUnit) diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/state/machine.go juju-core-1.18.1/src/launchpad.net/juju-core/state/machine.go --- juju-core-1.18.0/src/launchpad.net/juju-core/state/machine.go 2014-04-04 16:57:31.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/state/machine.go 2014-04-11 09:55:10.000000000 +0000 @@ -20,6 +20,7 @@ "launchpad.net/juju-core/state/presence" "launchpad.net/juju-core/tools" "launchpad.net/juju-core/utils" + "launchpad.net/juju-core/utils/set" "launchpad.net/juju-core/version" ) @@ -814,25 +815,26 @@ } func mergedAddresses(machineAddresses, providerAddresses []address) []instance.Address { - merged := make(map[string]instance.Address) - for _, address := range machineAddresses { - merged[address.Value] = address.InstanceAddress() - } - for _, address := range providerAddresses { - merged[address.Value] = address.InstanceAddress() + merged := make([]instance.Address, len(providerAddresses), len(providerAddresses)+len(machineAddresses)) + var providerValues set.Strings + for i, address := range providerAddresses { + providerValues.Add(address.Value) + merged[i] = address.InstanceAddress() } - addresses := make([]instance.Address, 0, len(merged)) - for _, address := range merged { - addresses = append(addresses, address) + for _, address := range machineAddresses { + if !providerValues.Contains(address.Value) { + merged = append(merged, address.InstanceAddress()) + } } - return addresses + return merged } // Addresses returns any hostnames and ips associated with a machine, // determined both by the machine itself, and by asking the provider. // // The addresses returned by the provider shadow any of the addresses -// that the machine reported with the same address value. +// that the machine reported with the same address value. Provider-reported +// addresses always come before machine-reported addresses. func (m *Machine) Addresses() (addresses []instance.Address) { return mergedAddresses(m.doc.MachineAddresses, m.doc.Addresses) } @@ -868,7 +870,7 @@ // SetMachineAddresses records any addresses related to the machine, sourced // by asking the machine. -func (m *Machine) SetMachineAddresses(addresses []instance.Address) (err error) { +func (m *Machine) SetMachineAddresses(addresses ...instance.Address) (err error) { stateAddresses := instanceAddressesToAddresses(addresses) ops := []txn.Op{ { diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/state/machine_test.go juju-core-1.18.1/src/launchpad.net/juju-core/state/machine_test.go --- juju-core-1.18.0/src/launchpad.net/juju-core/state/machine_test.go 2014-04-04 16:57:31.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/state/machine_test.go 2014-04-11 09:55:10.000000000 +0000 @@ -1237,13 +1237,42 @@ instance.NewAddress("127.0.0.1"), instance.NewAddress("8.8.8.8"), } - err = machine.SetMachineAddresses(addresses) + err = machine.SetMachineAddresses(addresses...) c.Assert(err, gc.IsNil) err = machine.Refresh() c.Assert(err, gc.IsNil) c.Assert(machine.MachineAddresses(), gc.DeepEquals, addresses) } +func (s *MachineSuite) TestMergedAddresses(c *gc.C) { + machine, err := s.State.AddMachine("quantal", state.JobHostUnits) + c.Assert(err, gc.IsNil) + c.Assert(machine.Addresses(), gc.HasLen, 0) + + addresses := []instance.Address{ + instance.NewAddress("127.0.0.1"), + instance.NewAddress("8.8.8.8"), + } + addresses[0].NetworkName = "loopback" + err = machine.SetAddresses(addresses) + c.Assert(err, gc.IsNil) + + machineAddresses := []instance.Address{ + instance.NewAddress("127.0.0.1"), + instance.NewAddress("192.168.0.1"), + } + err = machine.SetMachineAddresses(machineAddresses...) + c.Assert(err, gc.IsNil) + err = machine.Refresh() + c.Assert(err, gc.IsNil) + + c.Assert(machine.Addresses(), gc.DeepEquals, []instance.Address{ + addresses[0], + addresses[1], + machineAddresses[1], + }) +} + func (s *MachineSuite) addMachineWithSupportedContainer(c *gc.C, container instance.ContainerType) *state.Machine { machine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/state/testing/agent.go juju-core-1.18.1/src/launchpad.net/juju-core/state/testing/agent.go --- juju-core-1.18.0/src/launchpad.net/juju-core/state/testing/agent.go 2014-04-04 16:53:35.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/state/testing/agent.go 2014-04-11 09:53:38.000000000 +0000 @@ -10,6 +10,8 @@ // SetAgentVersion sets the current agent version in the state's // environment configuration. +// This is similar to state.SetEnvironAgentVersion but it doesn't require that +// the environment have all agents at the same version already. func SetAgentVersion(st *state.State, vers version.Number) error { return st.UpdateEnvironConfig(map[string]interface{}{"agent-version": vers.String()}, nil, nil) } diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/state/unit.go juju-core-1.18.1/src/launchpad.net/juju-core/state/unit.go --- juju-core-1.18.0/src/launchpad.net/juju-core/state/unit.go 2014-04-04 16:57:31.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/state/unit.go 2014-04-11 09:55:10.000000000 +0000 @@ -479,6 +479,27 @@ return names } +// JoinedRelations returns the relations for which the unit is in scope. +func (u *Unit) JoinedRelations() ([]*Relation, error) { + candidates, err := serviceRelations(u.st, u.doc.Service) + if err != nil { + return nil, err + } + var joinedRelations []*Relation + for _, relation := range candidates { + relationUnit, err := relation.Unit(u) + if err != nil { + return nil, err + } + if inScope, err := relationUnit.InScope(); err != nil { + return nil, err + } else if inScope { + joinedRelations = append(joinedRelations, relation) + } + } + return joinedRelations, nil +} + // DeployerTag returns the tag of the agent responsible for deploying // the unit. If no such entity can be determined, false is returned. func (u *Unit) DeployerTag() (string, bool) { diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/state/unit_test.go juju-core-1.18.1/src/launchpad.net/juju-core/state/unit_test.go --- juju-core-1.18.0/src/launchpad.net/juju-core/state/unit_test.go 2014-04-04 16:57:31.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/state/unit_test.go 2014-04-11 09:55:10.000000000 +0000 @@ -278,14 +278,39 @@ c.Assert(ok, gc.Equals, false) addresses := []instance.Address{ - instance.NewAddress("127.0.0.1"), + instance.NewAddress("10.0.0.1"), instance.NewAddress("8.8.8.8"), } err = machine.SetAddresses(addresses) c.Assert(err, gc.IsNil) address, ok = s.unit.PrivateAddress() - c.Check(address, gc.Equals, "127.0.0.1") + c.Check(address, gc.Equals, "10.0.0.1") + c.Assert(ok, gc.Equals, true) +} + +func (s *UnitSuite) TestPublicAddressMachineAddresses(c *gc.C) { + machine, err := s.State.AddMachine("quantal", state.JobHostUnits) + c.Assert(err, gc.IsNil) + err = s.unit.AssignToMachine(machine) + c.Assert(err, gc.IsNil) + + publicProvider := instance.NewAddress("8.8.8.8") + privateProvider := instance.NewAddress("10.0.0.1") + privateMachine := instance.NewAddress("10.0.0.2") + + err = machine.SetAddresses([]instance.Address{privateProvider}) + c.Assert(err, gc.IsNil) + err = machine.SetMachineAddresses(privateMachine) + c.Assert(err, gc.IsNil) + address, ok := s.unit.PublicAddress() + c.Check(address, gc.Equals, "10.0.0.1") + c.Assert(ok, gc.Equals, true) + + err = machine.SetAddresses([]instance.Address{publicProvider, privateProvider}) + c.Assert(err, gc.IsNil) + address, ok = s.unit.PublicAddress() + c.Check(address, gc.Equals, "8.8.8.8") c.Assert(ok, gc.Equals, true) } @@ -1039,6 +1064,47 @@ c.Assert(principal, gc.Equals, "") } +func (s *UnitSuite) TestJoinedRelations(c *gc.C) { + wordpress0 := s.unit + mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) + mysql0, err := mysql.AddUnit() + c.Assert(err, gc.IsNil) + eps, err := s.State.InferEndpoints([]string{"wordpress", "mysql"}) + c.Assert(err, gc.IsNil) + rel, err := s.State.AddRelation(eps...) + c.Assert(err, gc.IsNil) + + assertJoinedRelations := func(unit *state.Unit, expect ...*state.Relation) { + actual, err := unit.JoinedRelations() + c.Assert(err, gc.IsNil) + c.Assert(actual, gc.HasLen, len(expect)) + for i, a := range actual { + c.Assert(a.Id(), gc.Equals, expect[i].Id()) + } + } + assertJoinedRelations(wordpress0) + assertJoinedRelations(mysql0) + + mysql0ru, err := rel.Unit(mysql0) + c.Assert(err, gc.IsNil) + err = mysql0ru.EnterScope(nil) + c.Assert(err, gc.IsNil) + assertJoinedRelations(wordpress0) + assertJoinedRelations(mysql0, rel) + + wordpress0ru, err := rel.Unit(wordpress0) + c.Assert(err, gc.IsNil) + err = wordpress0ru.EnterScope(nil) + c.Assert(err, gc.IsNil) + assertJoinedRelations(wordpress0, rel) + assertJoinedRelations(mysql0, rel) + + err = mysql0ru.LeaveScope() + c.Assert(err, gc.IsNil) + assertJoinedRelations(wordpress0, rel) + assertJoinedRelations(mysql0) +} + func (s *UnitSuite) TestRemove(c *gc.C) { err := s.unit.Remove() c.Assert(err, gc.ErrorMatches, `cannot remove unit "wordpress/0": unit is not dead`) diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/store/server_test.go juju-core-1.18.1/src/launchpad.net/juju-core/store/server_test.go --- juju-core-1.18.0/src/launchpad.net/juju-core/store/server_test.go 2014-04-04 16:53:35.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/store/server_test.go 2014-04-11 09:53:38.000000000 +0000 @@ -203,6 +203,10 @@ // checkCounterSum checks that statistics are properly collected. // It retries a few times as they are generally collected in background. func (s *StoreSuite) checkCounterSum(c *gc.C, key []string, prefix bool, expected int64) { + if *noTestMongoJs { + c.Skip("MongoDB javascript not available") + } + var sum int64 for retry := 0; retry < 10; retry++ { time.Sleep(1e8) @@ -298,6 +302,10 @@ } func (s *StoreSuite) TestStatsCounter(c *gc.C) { + if *noTestMongoJs { + c.Skip("MongoDB javascript not available") + } + for _, key := range [][]string{{"a", "b"}, {"a", "b"}, {"a", "c"}, {"a"}} { err := s.store.IncCounter(key) c.Assert(err, gc.IsNil) @@ -328,6 +336,10 @@ } func (s *StoreSuite) TestStatsCounterList(c *gc.C) { + if *noTestMongoJs { + c.Skip("MongoDB javascript not available") + } + incs := [][]string{ {"a"}, {"a", "b"}, @@ -376,6 +388,10 @@ } func (s *StoreSuite) TestStatsCounterBy(c *gc.C) { + if *noTestMongoJs { + c.Skip("MongoDB javascript not available") + } + incs := []struct { key []string day int diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/store/store_test.go juju-core-1.18.1/src/launchpad.net/juju-core/store/store_test.go --- juju-core-1.18.0/src/launchpad.net/juju-core/store/store_test.go 2014-04-04 16:53:35.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/store/store_test.go 2014-04-11 09:53:38.000000000 +0000 @@ -4,9 +4,11 @@ package store_test import ( + "flag" "fmt" "io" "io/ioutil" + "os" "strconv" "sync" stdtesting "testing" @@ -35,12 +37,19 @@ store *store.Store } +var noTestMongoJs *bool = flag.Bool("notest-mongojs", false, "Disable MongoDB tests that require javascript") + type TrivialSuite struct{} func (s *StoreSuite) SetUpSuite(c *gc.C) { s.MgoSuite.SetUpSuite(c) s.HTTPSuite.SetUpSuite(c) s.LoggingSuite.SetUpSuite(c) + + if os.Getenv("JUJU_NOTEST_MONGOJS") == "1" { + c.Log("Tests requiring MongoDB Javascript will be skipped") + *noTestMongoJs = true + } } func (s *StoreSuite) TearDownSuite(c *gc.C) { @@ -601,6 +610,10 @@ } func (s *StoreSuite) TestSumCounters(c *gc.C) { + if *noTestMongoJs { + c.Skip("MongoDB javascript not available") + } + req := store.CounterRequest{Key: []string{"a"}} cs, err := s.store.Counters(&req) c.Assert(err, gc.IsNil) @@ -674,6 +687,10 @@ } func (s *StoreSuite) TestCountersReadOnlySum(c *gc.C) { + if *noTestMongoJs { + c.Skip("MongoDB javascript not available") + } + // Summing up an unknown key shouldn't add the key to the database. req := store.CounterRequest{Key: []string{"a", "b", "c"}} _, err := s.store.Counters(&req) @@ -686,6 +703,10 @@ } func (s *StoreSuite) TestCountersTokenCaching(c *gc.C) { + if *noTestMongoJs { + c.Skip("MongoDB javascript not available") + } + assertSum := func(i int, want int64) { req := store.CounterRequest{Key: []string{strconv.Itoa(i)}} cs, err := s.store.Counters(&req) @@ -741,6 +762,10 @@ } func (s *StoreSuite) TestCounterTokenUniqueness(c *gc.C) { + if *noTestMongoJs { + c.Skip("MongoDB javascript not available") + } + var wg0, wg1 sync.WaitGroup wg0.Add(10) wg1.Add(10) @@ -762,6 +787,10 @@ } func (s *StoreSuite) TestListCounters(c *gc.C) { + if *noTestMongoJs { + c.Skip("MongoDB javascript not available") + } + incs := [][]string{ {"c", "b", "a"}, // Assign internal id c < id b < id a, to make sorting slightly trickier. {"a"}, @@ -823,6 +852,10 @@ } func (s *StoreSuite) TestListCountersBy(c *gc.C) { + if *noTestMongoJs { + c.Skip("MongoDB javascript not available") + } + incs := []struct { key []string day int diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/testing/filetesting/filetesting.go juju-core-1.18.1/src/launchpad.net/juju-core/testing/filetesting/filetesting.go --- juju-core-1.18.0/src/launchpad.net/juju-core/testing/filetesting/filetesting.go 1970-01-01 00:00:00.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/testing/filetesting/filetesting.go 2014-04-11 09:55:10.000000000 +0000 @@ -0,0 +1,198 @@ +// Copyright 2013, 2014 Canonical Ltd. +// Licensed under the LGPLv3, see LICENCE file for details. + +package filetesting + +import ( + "io/ioutil" + "os" + "path/filepath" + + jc "github.com/juju/testing/checkers" + gc "launchpad.net/gocheck" + + "launchpad.net/juju-core/utils" +) + +// Entry represents a filesystem entity that can be created; and whose +// correctness can be verified. +type Entry interface { + + // GetPath returns the slash-separated relative path that this + // entry represents. + GetPath() string + + // Create causes the entry to be created, relative to basePath. It returns + // a copy of the receiver. + Create(c *gc.C, basePath string) Entry + + // Check checks that the entry exists, relative to basePath, and matches + // the entry that would be created by Create. It returns a copy of the + // receiver. + Check(c *gc.C, basePath string) Entry +} + +var ( + _ Entry = Dir{} + _ Entry = File{} + _ Entry = Symlink{} + _ Entry = Removed{} +) + +// Entries supplies convenience methods on Entry slices. +type Entries []Entry + +// Paths returns the slash-separated path of every entry. +func (e Entries) Paths() []string { + result := make([]string, len(e)) + for i, entry := range e { + result[i] = entry.GetPath() + } + return result +} + +// Create creates every entry relative to basePath and returns a copy of itself. +func (e Entries) Create(c *gc.C, basePath string) Entries { + result := make([]Entry, len(e)) + for i, entry := range e { + result[i] = entry.Create(c, basePath) + } + return result +} + +// Check checks every entry relative to basePath and returns a copy of itself. +func (e Entries) Check(c *gc.C, basePath string) Entries { + result := make([]Entry, len(e)) + for i, entry := range e { + result[i] = entry.Check(c, basePath) + } + return result +} + +// AsRemoveds returns a slice of Removed entries whose paths correspond to +// those in e. +func (e Entries) AsRemoveds() Entries { + result := make([]Entry, len(e)) + for i, entry := range e { + result[i] = Removed{entry.GetPath()} + } + return result +} + +// join joins a slash-separated path to a filesystem basePath. +func join(basePath, path string) string { + return filepath.Join(basePath, filepath.FromSlash(path)) +} + +// Dir is an Entry that allows directories to be created and verified. The +// Path field should use "/" as the path separator. +type Dir struct { + Path string + Perm os.FileMode +} + +func (d Dir) GetPath() string { + return d.Path +} + +func (d Dir) Create(c *gc.C, basePath string) Entry { + path := join(basePath, d.Path) + err := os.MkdirAll(path, d.Perm) + c.Assert(err, gc.IsNil) + err = os.Chmod(path, d.Perm) + c.Assert(err, gc.IsNil) + return d +} + +func (d Dir) Check(c *gc.C, basePath string) Entry { + fileInfo, err := os.Lstat(join(basePath, d.Path)) + if !c.Check(err, gc.IsNil) { + return d + } + c.Check(fileInfo.Mode()&os.ModePerm, gc.Equals, d.Perm) + c.Check(fileInfo.Mode()&os.ModeType, gc.Equals, os.ModeDir) + return d +} + +// File is an Entry that allows plain files to be created and verified. The +// Path field should use "/" as the path separator. +type File struct { + Path string + Data string + Perm os.FileMode +} + +func (f File) GetPath() string { + return f.Path +} + +func (f File) Create(c *gc.C, basePath string) Entry { + err := ioutil.WriteFile(join(basePath, f.Path), []byte(f.Data), f.Perm) + c.Assert(err, gc.IsNil) + return f +} + +func (f File) Check(c *gc.C, basePath string) Entry { + path := join(basePath, f.Path) + fileInfo, err := os.Lstat(path) + if !c.Check(err, gc.IsNil) { + return f + } + mode := fileInfo.Mode() + c.Check(mode&os.ModeType, gc.Equals, os.FileMode(0)) + c.Check(mode&os.ModePerm, gc.Equals, f.Perm) + data, err := ioutil.ReadFile(path) + c.Check(err, gc.IsNil) + c.Check(string(data), gc.Equals, f.Data) + return f +} + +// Symlink is an Entry that allows symlinks to be created and verified. The +// Path field should use "/" as the path separator. +type Symlink struct { + Path string + Link string +} + +func (s Symlink) GetPath() string { + return s.Path +} + +func (s Symlink) Create(c *gc.C, basePath string) Entry { + err := os.Symlink(s.Link, join(basePath, s.Path)) + c.Assert(err, gc.IsNil) + return s +} + +func (s Symlink) Check(c *gc.C, basePath string) Entry { + link, err := os.Readlink(join(basePath, s.Path)) + c.Check(err, gc.IsNil) + c.Check(link, gc.Equals, s.Link) + return s +} + +// Removed is an Entry that indicates the absence of any entry. The Path +// field should use "/" as the path separator. +type Removed struct { + Path string +} + +func (r Removed) GetPath() string { + return r.Path +} + +func (r Removed) Create(c *gc.C, basePath string) Entry { + err := os.RemoveAll(join(basePath, r.Path)) + c.Assert(err, gc.IsNil) + return r +} + +func (r Removed) Check(c *gc.C, basePath string) Entry { + _, err := os.Lstat(join(basePath, r.Path)) + // utils.IsNotExist allows us to handle the following case: + // File{"foo", ...}.Create(...) + // Removed{"foo/bar"}.Check(...) + // ...where os.IsNotExist would not work. + c.Assert(err, jc.Satisfies, utils.IsNotExist) + return r +} diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/testing/filetesting/filetesting_test.go juju-core-1.18.1/src/launchpad.net/juju-core/testing/filetesting/filetesting_test.go --- juju-core-1.18.0/src/launchpad.net/juju-core/testing/filetesting/filetesting_test.go 1970-01-01 00:00:00.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/testing/filetesting/filetesting_test.go 2014-04-11 09:55:10.000000000 +0000 @@ -0,0 +1,285 @@ +// Copyright 2014 Canonical Ltd. +// Licensed under the LGPLv3, see LICENCE file for details. + +package filetesting_test + +import ( + "io/ioutil" + "os" + "path/filepath" + + jc "github.com/juju/testing/checkers" + gc "launchpad.net/gocheck" + + ft "launchpad.net/juju-core/testing/filetesting" + "launchpad.net/juju-core/testing/testbase" +) + +type EntrySuite struct { + testbase.LoggingSuite + basePath string +} + +var _ = gc.Suite(&EntrySuite{}) + +func (s *EntrySuite) SetUpTest(c *gc.C) { + s.LoggingSuite.SetUpTest(c) + s.basePath = c.MkDir() +} + +func (s *EntrySuite) join(path string) string { + return filepath.Join(s.basePath, filepath.FromSlash(path)) +} + +func (s *EntrySuite) TestFileCreate(c *gc.C) { + ft.File{"foobar", "hello", 0644}.Create(c, s.basePath) + path := s.join("foobar") + info, err := os.Lstat(path) + c.Assert(err, gc.IsNil) + c.Assert(info.Mode()&os.ModePerm, gc.Equals, os.FileMode(0644)) + c.Assert(info.Mode()&os.ModeType, gc.Equals, os.FileMode(0)) + data, err := ioutil.ReadFile(path) + c.Assert(err, gc.IsNil) + c.Assert(string(data), gc.Equals, "hello") +} + +func (s *EntrySuite) TestFileCreateFailure(c *gc.C) { + c.ExpectFailure("should fail to create file in missing dir") + ft.File{"missing/foobar", "hello", 0644}.Create(c, s.basePath) +} + +func (s *EntrySuite) TestFileCheckSuccess(c *gc.C) { + ft.File{"furble", "pingle", 0740}.Create(c, s.basePath) + ft.File{"furble", "pingle", 0740}.Check(c, s.basePath) +} + +func (s *EntrySuite) TestFileCheckFailureBadPerm(c *gc.C) { + ft.File{"furble", "pingle", 0644}.Create(c, s.basePath) + c.ExpectFailure("shouldn't pass with different perms") + ft.File{"furble", "pingle", 0740}.Check(c, s.basePath) +} + +func (s *EntrySuite) TestFileCheckFailureBadData(c *gc.C) { + ft.File{"furble", "pingle", 0740}.Check(c, s.basePath) + c.ExpectFailure("shouldn't pass with different content") + ft.File{"furble", "wrongle", 0740}.Check(c, s.basePath) +} + +func (s *EntrySuite) TestFileCheckFailureNoExist(c *gc.C) { + c.ExpectFailure("shouldn't find file that does not exist") + ft.File{"furble", "pingle", 0740}.Check(c, s.basePath) +} + +func (s *EntrySuite) TestFileCheckFailureSymlink(c *gc.C) { + ft.Symlink{"link", "file"}.Create(c, s.basePath) + ft.File{"file", "content", 0644}.Create(c, s.basePath) + c.ExpectFailure("shouldn't accept symlink, even if pointing to matching file") + ft.File{"link", "content", 0644}.Check(c, s.basePath) +} + +func (s *EntrySuite) TestFileCheckFailureDir(c *gc.C) { + ft.Dir{"furble", 0740}.Create(c, s.basePath) + c.ExpectFailure("shouldn't accept dir") + ft.File{"furble", "pingle", 0740}.Check(c, s.basePath) +} + +func (s *EntrySuite) TestDirCreate(c *gc.C) { + ft.Dir{"path", 0750}.Create(c, s.basePath) + info, err := os.Lstat(s.join("path")) + c.Check(err, gc.IsNil) + c.Check(info.Mode()&os.ModePerm, gc.Equals, os.FileMode(0750)) + c.Check(info.Mode()&os.ModeType, gc.Equals, os.ModeDir) +} + +func (s *EntrySuite) TestDirCreateChmod(c *gc.C) { + ft.Dir{"name", 0750}.Create(c, s.basePath) + expect := ft.Dir{"name", 0755}.Create(c, s.basePath) + expect.Check(c, s.basePath) +} + +func (s *EntrySuite) TestDirCreateSubdir(c *gc.C) { + subdir := ft.Dir{"some/path", 0750}.Create(c, s.basePath) + subdir.Check(c, s.basePath) + ft.Dir{"some", 0750}.Check(c, s.basePath) +} + +func (s *EntrySuite) TestDirCreateFailure(c *gc.C) { + os.Chmod(s.basePath, 0444) + defer os.Chmod(s.basePath, 0777) + c.ExpectFailure("should fail to create file") + ft.Dir{"foobar", 0750}.Create(c, s.basePath) +} + +func (s *EntrySuite) TestDirCheck(c *gc.C) { + ft.Dir{"fooble", 0751}.Create(c, s.basePath) + ft.Dir{"fooble", 0751}.Check(c, s.basePath) +} + +func (s *EntrySuite) TestDirCheckFailureNoExist(c *gc.C) { + c.ExpectFailure("shouldn't find dir that does not exist") + ft.Dir{"fooble", 0751}.Check(c, s.basePath) +} + +func (s *EntrySuite) TestDirCheckFailureBadPerm(c *gc.C) { + ft.Dir{"furble", 0740}.Check(c, s.basePath) + c.ExpectFailure("shouldn't pass with different perms") + ft.Dir{"furble", 0755}.Check(c, s.basePath) +} + +func (s *EntrySuite) TestDirCheckFailureSymlink(c *gc.C) { + ft.Symlink{"link", "dir"}.Create(c, s.basePath) + ft.Dir{"dir", 0644}.Create(c, s.basePath) + c.ExpectFailure("shouldn't accept symlink, even if pointing to matching dir") + ft.Dir{"link", 0644}.Check(c, s.basePath) +} + +func (s *EntrySuite) TestDirCheckFailureFile(c *gc.C) { + ft.File{"blah", "content", 0644}.Create(c, s.basePath) + c.ExpectFailure("shouldn't accept file") + ft.Dir{"blah", 0644}.Check(c, s.basePath) +} + +func (s *EntrySuite) TestSymlinkCreate(c *gc.C) { + ft.Symlink{"link", "target"}.Create(c, s.basePath) + target, err := os.Readlink(s.join("link")) + c.Assert(err, gc.IsNil) + c.Assert(target, gc.Equals, "target") +} + +func (s *EntrySuite) TestSymlinkCreateFailure(c *gc.C) { + c.ExpectFailure("should fail to create symlink in missing dir") + ft.Symlink{"missing/link", "target"}.Create(c, s.basePath) +} + +func (s *EntrySuite) TestSymlinkCheck(c *gc.C) { + ft.Symlink{"link", "target"}.Create(c, s.basePath) + ft.Symlink{"link", "target"}.Check(c, s.basePath) +} + +func (s *EntrySuite) TestSymlinkCheckFailureNoExist(c *gc.C) { + c.ExpectFailure("should not accept symlink that doesn't exist") + ft.Symlink{"link", "target"}.Check(c, s.basePath) +} + +func (s *EntrySuite) TestSymlinkCheckFailureBadTarget(c *gc.C) { + ft.Symlink{"link", "target"}.Create(c, s.basePath) + c.ExpectFailure("should not accept different target") + ft.Symlink{"link", "different"}.Check(c, s.basePath) +} + +func (s *EntrySuite) TestSymlinkCheckFailureFile(c *gc.C) { + ft.File{"link", "target", 0644}.Create(c, s.basePath) + c.ExpectFailure("should not accept plain file") + ft.Symlink{"link", "target"}.Check(c, s.basePath) +} + +func (s *EntrySuite) TestSymlinkCheckFailureDir(c *gc.C) { + ft.Dir{"link", 0755}.Create(c, s.basePath) + c.ExpectFailure("should not accept dir") + ft.Symlink{"link", "different"}.Check(c, s.basePath) +} + +func (s *EntrySuite) TestRemovedCreate(c *gc.C) { + ft.File{"some-file", "content", 0644}.Create(c, s.basePath) + ft.Removed{"some-file"}.Create(c, s.basePath) + _, err := os.Lstat(s.join("some-file")) + c.Assert(err, jc.Satisfies, os.IsNotExist) +} + +func (s *EntrySuite) TestRemovedCreateNothing(c *gc.C) { + ft.Removed{"some-file"}.Create(c, s.basePath) + _, err := os.Lstat(s.join("some-file")) + c.Assert(err, jc.Satisfies, os.IsNotExist) +} + +func (s *EntrySuite) TestRemovedCreateFailure(c *gc.C) { + ft.File{"some-file", "content", 0644}.Create(c, s.basePath) + os.Chmod(s.basePath, 0444) + defer os.Chmod(s.basePath, 0777) + c.ExpectFailure("should fail to remove file") + ft.Removed{"some-file"}.Create(c, s.basePath) +} + +func (s *EntrySuite) TestRemovedCheck(c *gc.C) { + ft.Removed{"some-file"}.Check(c, s.basePath) +} + +func (s *EntrySuite) TestRemovedCheckParentNotDir(c *gc.C) { + ft.File{"some-dir", "lol-not-a-file", 0644}.Create(c, s.basePath) + ft.Removed{"some-dir/some-file"}.Check(c, s.basePath) +} + +func (s *EntrySuite) TestRemovedCheckFailureFile(c *gc.C) { + ft.File{"some-file", "", 0644}.Create(c, s.basePath) + c.ExpectFailure("should not accept file") + ft.Removed{"some-file"}.Check(c, s.basePath) +} + +func (s *EntrySuite) TestRemovedCheckFailureDir(c *gc.C) { + ft.Dir{"some-dir", 0755}.Create(c, s.basePath) + c.ExpectFailure("should not accept dir") + ft.Removed{"some-dir"}.Check(c, s.basePath) +} + +func (s *EntrySuite) TestRemovedCheckFailureSymlink(c *gc.C) { + ft.Symlink{"some-link", "target"}.Create(c, s.basePath) + c.ExpectFailure("should not accept symlink") + ft.Removed{"some-link"}.Check(c, s.basePath) +} + +func (s *EntrySuite) TestCreateCheckChainResults(c *gc.C) { + for i, test := range (ft.Entries{ + ft.File{"some-file", "content", 0644}, + ft.Dir{"some-dir", 0750}, + ft.Symlink{"some-link", "target"}, + ft.Removed{"missing"}, + }) { + c.Logf("test %d: %#v", i, test) + chained := test.Create(c, s.basePath) + chained = chained.Check(c, s.basePath) + c.Assert(chained, jc.DeepEquals, test) + } +} + +func (s *EntrySuite) TestEntries(c *gc.C) { + initial := ft.Entries{ + ft.File{"some-file", "content", 0600}, + ft.Dir{"some-dir", 0750}, + ft.Symlink{"some-link", "target"}, + ft.Removed{"missing"}, + } + expectRemoveds := ft.Entries{ + ft.Removed{"some-file"}, + ft.Removed{"some-dir"}, + ft.Removed{"some-link"}, + ft.Removed{"missing"}, + } + removeds := initial.AsRemoveds() + c.Assert(removeds, jc.DeepEquals, expectRemoveds) + + expectPaths := []string{"some-file", "some-dir", "some-link", "missing"} + c.Assert(initial.Paths(), jc.DeepEquals, expectPaths) + c.Assert(removeds.Paths(), jc.DeepEquals, expectPaths) + + chainRemoveds := initial.Create(c, s.basePath).Check(c, s.basePath).AsRemoveds() + c.Assert(chainRemoveds, jc.DeepEquals, expectRemoveds) + chainRemoveds = chainRemoveds.Create(c, s.basePath).Check(c, s.basePath) + c.Assert(chainRemoveds, jc.DeepEquals, expectRemoveds) +} + +func (s *EntrySuite) TestEntriesCreateFailure(c *gc.C) { + c.ExpectFailure("cannot create an entry") + ft.Entries{ + ft.File{"good", "good", 0750}, + ft.File{"nodir/bad", "bad", 0640}, + }.Create(c, s.basePath) +} + +func (s *EntrySuite) TestEntriesCheckFailure(c *gc.C) { + goodFile := ft.File{"good", "good", 0751}.Create(c, s.basePath) + c.ExpectFailure("entry does not exist") + ft.Entries{ + goodFile, + ft.File{"bad", "", 0750}, + }.Check(c, s.basePath) +} diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/testing/filetesting/package_test.go juju-core-1.18.1/src/launchpad.net/juju-core/testing/filetesting/package_test.go --- juju-core-1.18.0/src/launchpad.net/juju-core/testing/filetesting/package_test.go 1970-01-01 00:00:00.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/testing/filetesting/package_test.go 2014-04-11 09:55:10.000000000 +0000 @@ -0,0 +1,14 @@ +// Copyright 2013, 2014 Canonical Ltd. +// Licensed under the LGPLv3, see LICENCE file for details. + +package filetesting_test + +import ( + "testing" + + gc "launchpad.net/gocheck" +) + +func Test(t *testing.T) { + gc.TestingT(t) +} diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/utils/file_test.go juju-core-1.18.1/src/launchpad.net/juju-core/utils/file_test.go --- juju-core-1.18.0/src/launchpad.net/juju-core/utils/file_test.go 2014-04-04 16:57:31.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/utils/file_test.go 2014-04-11 09:53:38.000000000 +0000 @@ -185,3 +185,16 @@ c.Assert(os.Remove(path), gc.IsNil) } } + +func (*fileSuite) TestIsNotExist(c *gc.C) { + dir := c.MkDir() + path := func(s string) string { return filepath.Join(dir, s) } + err := ioutil.WriteFile(path("file"), []byte("blah"), 0644) + c.Assert(err, gc.IsNil) + + _, err = os.Lstat(path("noexist")) + c.Assert(err, jc.Satisfies, utils.IsNotExist) + + _, err = os.Lstat(path("file/parent-not-a-dir")) + c.Assert(err, jc.Satisfies, utils.IsNotExist) +} diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/utils/file_unix.go juju-core-1.18.1/src/launchpad.net/juju-core/utils/file_unix.go --- juju-core-1.18.0/src/launchpad.net/juju-core/utils/file_unix.go 2014-04-04 16:57:31.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/utils/file_unix.go 2014-04-11 09:53:38.000000000 +0000 @@ -6,6 +6,7 @@ import ( "os" + "syscall" ) // ReplaceFile atomically replaces the destination file or directory @@ -14,3 +15,20 @@ func ReplaceFile(source, destination string) error { return os.Rename(source, destination) } + +// IsNotExist returns true if the error is consistent with an attempt to +// reference a file that does not exist. This works around the occasionally +// unhelpful behaviour of os.IsNotExist, which does not recognise the error +// produced when trying to read a path in which some component appears to +// reference a directory but actually references a file. For example, if +// "foo" is a file, an attempt to read "foo/bar" will generate an error that +// does not satisfy os.IsNotExist, but will satisfy utils.IsNotExist. +func IsNotExist(err error) bool { + if os.IsNotExist(err) { + return true + } + if e, ok := err.(*os.PathError); ok && e.Err == syscall.ENOTDIR { + return true + } + return false +} diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/utils/file_windows.go juju-core-1.18.1/src/launchpad.net/juju-core/utils/file_windows.go --- juju-core-1.18.0/src/launchpad.net/juju-core/utils/file_windows.go 2014-04-04 16:57:31.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/utils/file_windows.go 2014-04-11 09:53:38.000000000 +0000 @@ -33,3 +33,9 @@ } return nil } + +// IsNotExist returns true if the error is consistent with an attempt to +// reference a file that does not exist. +func IsNotExist(err error) bool { + return os.IsNotExist(err) +} diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/utils/ssh/ssh.go juju-core-1.18.1/src/launchpad.net/juju-core/utils/ssh/ssh.go --- juju-core-1.18.0/src/launchpad.net/juju-core/utils/ssh/ssh.go 2014-04-04 16:53:35.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/utils/ssh/ssh.go 2014-04-11 09:53:38.000000000 +0000 @@ -81,7 +81,7 @@ // Paths are specified in the scp format, [[user@]host:]path. If // any extra arguments are specified in extraArgs, they are passed // verbatim. - Copy(targets, extraArgs []string, options *Options) error + Copy(args []string, options *Options) error } // Cmd represents a command to be (or being) executed @@ -236,7 +236,7 @@ } // Copy is a short-cut for DefaultClient.Copy. -func Copy(targets, extraArgs []string, options *Options) error { +func Copy(args []string, options *Options) error { logger.Debugf("using %s ssh client", chosenClient) - return DefaultClient.Copy(targets, extraArgs, options) + return DefaultClient.Copy(args, options) } diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/utils/ssh/ssh_gocrypto.go juju-core-1.18.1/src/launchpad.net/juju-core/utils/ssh/ssh_gocrypto.go --- juju-core-1.18.0/src/launchpad.net/juju-core/utils/ssh/ssh_gocrypto.go 2014-04-04 16:57:31.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/utils/ssh/ssh_gocrypto.go 2014-04-11 09:55:10.000000000 +0000 @@ -62,7 +62,7 @@ // Copy implements Client.Copy. // // Copy is currently unimplemented, and will always return an error. -func (c *GoCryptoClient) Copy(targets, extraArgs []string, options *Options) error { +func (c *GoCryptoClient) Copy(args []string, options *Options) error { return fmt.Errorf("scp command is not implemented (OpenSSH scp not available in PATH)") } diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/utils/ssh/ssh_gocrypto_test.go juju-core-1.18.1/src/launchpad.net/juju-core/utils/ssh/ssh_gocrypto_test.go --- juju-core-1.18.0/src/launchpad.net/juju-core/utils/ssh/ssh_gocrypto_test.go 2014-04-04 16:57:31.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/utils/ssh/ssh_gocrypto_test.go 2014-04-11 09:55:10.000000000 +0000 @@ -145,6 +145,6 @@ func (s *SSHGoCryptoCommandSuite) TestCopy(c *gc.C) { client, err := ssh.NewGoCryptoClient() c.Assert(err, gc.IsNil) - err = client.Copy([]string{"0.1.2.3:b", c.MkDir()}, nil, nil) + err = client.Copy([]string{"0.1.2.3:b", c.MkDir()}, nil) c.Assert(err, gc.ErrorMatches, `scp command is not implemented \(OpenSSH scp not available in PATH\)`) } diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/utils/ssh/ssh_openssh.go juju-core-1.18.1/src/launchpad.net/juju-core/utils/ssh/ssh_openssh.go --- juju-core-1.18.0/src/launchpad.net/juju-core/utils/ssh/ssh_openssh.go 2014-04-04 16:53:35.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/utils/ssh/ssh_openssh.go 2014-04-11 09:53:38.000000000 +0000 @@ -124,17 +124,16 @@ } // Copy implements Client.Copy. -func (c *OpenSSHClient) Copy(targets, extraArgs []string, userOptions *Options) error { +func (c *OpenSSHClient) Copy(args []string, userOptions *Options) error { var options Options if userOptions != nil { options = *userOptions options.allocatePTY = false // doesn't make sense for scp } - args := opensshOptions(&options, scpKind) - args = append(args, extraArgs...) - args = append(args, targets...) - bin, args := sshpassWrap("scp", args) - cmd := exec.Command(bin, args...) + allArgs := opensshOptions(&options, scpKind) + allArgs = append(allArgs, args...) + bin, allArgs := sshpassWrap("scp", allArgs) + cmd := exec.Command(bin, allArgs...) var stderr bytes.Buffer cmd.Stderr = &stderr logger.Debugf("running: %s %s", bin, utils.CommandString(args...)) diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/utils/ssh/ssh_test.go juju-core-1.18.1/src/launchpad.net/juju-core/utils/ssh/ssh_test.go --- juju-core-1.18.0/src/launchpad.net/juju-core/utils/ssh/ssh_test.go 2014-04-04 16:53:35.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/utils/ssh/ssh_test.go 2014-04-11 09:53:38.000000000 +0000 @@ -129,7 +129,7 @@ opts.AllowPasswordAuthentication() opts.SetIdentities("x", "y") opts.SetPort(2022) - err := s.client.Copy([]string{"/tmp/blah", "foo@bar.com:baz"}, nil, &opts) + err := s.client.Copy([]string{"/tmp/blah", "foo@bar.com:baz"}, &opts) c.Assert(err, gc.IsNil) out, err := ioutil.ReadFile(s.fakescp + ".args") c.Assert(err, gc.IsNil) @@ -137,11 +137,18 @@ c.Assert(string(out), gc.Equals, s.fakescp+" -o StrictHostKeyChecking no -i x -i y -P 2022 /tmp/blah foo@bar.com:baz\n") // Try passing extra args - err = s.client.Copy([]string{"/tmp/blah", "foo@bar.com:baz"}, []string{"-r", "-v"}, &opts) + err = s.client.Copy([]string{"/tmp/blah", "foo@bar.com:baz", "-r", "-v"}, &opts) c.Assert(err, gc.IsNil) out, err = ioutil.ReadFile(s.fakescp + ".args") c.Assert(err, gc.IsNil) - c.Assert(string(out), gc.Equals, s.fakescp+" -o StrictHostKeyChecking no -i x -i y -P 2022 -r -v /tmp/blah foo@bar.com:baz\n") + c.Assert(string(out), gc.Equals, s.fakescp+" -o StrictHostKeyChecking no -i x -i y -P 2022 /tmp/blah foo@bar.com:baz -r -v\n") + + // Try interspersing extra args + err = s.client.Copy([]string{"-r", "/tmp/blah", "-v", "foo@bar.com:baz"}, &opts) + c.Assert(err, gc.IsNil) + out, err = ioutil.ReadFile(s.fakescp + ".args") + c.Assert(err, gc.IsNil) + c.Assert(string(out), gc.Equals, s.fakescp+" -o StrictHostKeyChecking no -i x -i y -P 2022 -r /tmp/blah -v foo@bar.com:baz\n") } func (s *SSHCommandSuite) TestCommandClientKeys(c *gc.C) { diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/version/version.go juju-core-1.18.1/src/launchpad.net/juju-core/version/version.go --- juju-core-1.18.0/src/launchpad.net/juju-core/version/version.go 2014-04-04 16:57:31.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/version/version.go 2014-04-11 09:55:10.000000000 +0000 @@ -23,7 +23,7 @@ // The presence and format of this constant is very important. // The debian/rules build recipe uses this value for the version // number of the release package. -const version = "1.18.0" +const version = "1.18.1" // lsbReleaseFile is the name of the file that is read in order to determine // the release version of ubuntu. diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/worker/machiner/machiner.go juju-core-1.18.1/src/launchpad.net/juju-core/worker/machiner/machiner.go --- juju-core-1.18.0/src/launchpad.net/juju-core/worker/machiner/machiner.go 2014-04-04 16:57:31.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/worker/machiner/machiner.go 2014-04-11 09:55:10.000000000 +0000 @@ -77,9 +77,6 @@ default: continue } - if ip.IsLoopback() { - continue - } hostAddresses = append(hostAddresses, instance.NewAddress(ip.String())) } if len(hostAddresses) == 0 { diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/worker/machiner/machiner_test.go juju-core-1.18.1/src/launchpad.net/juju-core/worker/machiner/machiner_test.go --- juju-core-1.18.0/src/launchpad.net/juju-core/worker/machiner/machiner_test.go 2014-04-04 16:57:31.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/worker/machiner/machiner_test.go 2014-04-11 09:55:10.000000000 +0000 @@ -140,9 +140,9 @@ s.PatchValue(machiner.InterfaceAddrs, func() ([]net.Addr, error) { addrs := []net.Addr{ &net.IPAddr{IP: net.IPv4(10, 0, 0, 1)}, - &net.IPAddr{IP: net.IPv4(127, 0, 0, 1)}, // loopback, ignored - &net.IPAddr{IP: net.IPv6loopback}, // loopback, ignored - &net.UnixAddr{}, // not IP, ignored + &net.IPAddr{IP: net.IPv4(127, 0, 0, 1)}, + &net.IPAddr{IP: net.IPv6loopback}, + &net.UnixAddr{}, // not IP, ignored &net.IPNet{IP: net.ParseIP("2001:db8::1")}, } return addrs, nil @@ -155,6 +155,8 @@ c.Assert(s.machine.Refresh(), gc.IsNil) c.Assert(s.machine.MachineAddresses(), gc.DeepEquals, []instance.Address{ instance.NewAddress("10.0.0.1"), + instance.NewAddress("127.0.0.1"), + instance.NewAddress("::1"), instance.NewAddress("2001:db8::1"), }) } diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/worker/peergrouper/worker_test.go juju-core-1.18.1/src/launchpad.net/juju-core/worker/peergrouper/worker_test.go --- juju-core-1.18.0/src/launchpad.net/juju-core/worker/peergrouper/worker_test.go 2014-04-04 16:57:31.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/worker/peergrouper/worker_test.go 2014-04-11 09:55:10.000000000 +0000 @@ -101,12 +101,8 @@ servers := make([][]instance.HostPort, n) for i := range servers { servers[i] = []instance.HostPort{{ - Address: instance.Address{ - Value: fmt.Sprintf("0.1.2.%d", i+10), - NetworkScope: instance.NetworkUnknown, - Type: instance.Ipv4Address, - }, - Port: apiPort, + Address: instance.NewAddress(fmt.Sprintf("0.1.2.%d", i+10)), + Port: apiPort, }} } return servers diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/worker/uniter/modes.go juju-core-1.18.1/src/launchpad.net/juju-core/worker/uniter/modes.go --- juju-core-1.18.0/src/launchpad.net/juju-core/worker/uniter/modes.go 2014-04-04 16:53:35.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/worker/uniter/modes.go 2014-04-11 09:53:38.000000000 +0000 @@ -22,16 +22,6 @@ // states of a running Uniter. type Mode func(u *Uniter) (Mode, error) -// ModeInit is the initial Uniter mode. -func ModeInit(u *Uniter) (next Mode, err error) { - defer modeContext("ModeInit", &err)() - logger.Infof("reconciling relation state") - if err := u.restoreRelations(); err != nil { - return nil, err - } - return ModeContinue, nil -} - // ModeContinue determines what action to take based on persistent uniter state. func ModeContinue(u *Uniter) (next Mode, err error) { defer modeContext("ModeContinue", &err)() diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/worker/uniter/relationer.go juju-core-1.18.1/src/launchpad.net/juju-core/worker/uniter/relationer.go --- juju-core-1.18.0/src/launchpad.net/juju-core/worker/uniter/relationer.go 2014-04-04 16:53:35.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/worker/uniter/relationer.go 2014-04-11 09:53:38.000000000 +0000 @@ -51,6 +51,12 @@ if r.dying { panic("dying relationer must not join!") } + // We need to make sure the state directory exists before we join the + // relation, lest a subsequent ReadAllStateDirs report local state that + // doesn't include relations recorded in remote state. + if err := r.dir.Ensure(); err != nil { + return err + } // uniter.RelationUnit.EnterScope() sets the unit's private address // internally automatically, so no need to set it here. return r.ru.EnterScope() @@ -127,10 +133,6 @@ if err = r.dir.State().Validate(hi); err != nil { return } - // We are about to use the dir, ensure it's there. - if err = r.dir.Ensure(); err != nil { - return - } if hi.Kind == hooks.RelationDeparted { r.ctx.DeleteMember(hi.RemoteUnit) } else if hi.RemoteUnit != "" { diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/worker/uniter/relationer_test.go juju-core-1.18.1/src/launchpad.net/juju-core/worker/uniter/relationer_test.go --- juju-core-1.18.0/src/launchpad.net/juju-core/worker/uniter/relationer_test.go 2014-04-04 16:53:35.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/worker/uniter/relationer_test.go 2014-04-11 09:55:10.000000000 +0000 @@ -4,8 +4,6 @@ package uniter_test import ( - "os" - "path/filepath" "strconv" "strings" "time" @@ -20,6 +18,7 @@ "launchpad.net/juju-core/state/api" apiuniter "launchpad.net/juju-core/state/api/uniter" coretesting "launchpad.net/juju-core/testing" + ft "launchpad.net/juju-core/testing/filetesting" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/worker/uniter" "launchpad.net/juju-core/worker/uniter/hook" @@ -83,6 +82,29 @@ return ru, u } +func (s *RelationerSuite) TestStateDir(c *gc.C) { + // Create the relationer; check its state dir is not created. + r := uniter.NewRelationer(s.apiRelUnit, s.dir, s.hooks) + path := strconv.Itoa(s.rel.Id()) + ft.Removed{path}.Check(c, s.dirPath) + + // Join the relation; check the dir was created. + err := r.Join() + c.Assert(err, gc.IsNil) + ft.Dir{path, 0755}.Check(c, s.dirPath) + + // Prepare to depart the relation; check the dir is still there. + hi := hook.Info{Kind: hooks.RelationBroken} + _, err = r.PrepareHook(hi) + c.Assert(err, gc.IsNil) + ft.Dir{path, 0755}.Check(c, s.dirPath) + + // Actually depart it; check the dir is removed. + err = r.CommitHook(hi) + c.Assert(err, gc.IsNil) + ft.Removed{path}.Check(c, s.dirPath) +} + func (s *RelationerSuite) TestEnterLeaveScope(c *gc.C) { ru1, _ := s.AddRelationUnit(c, "u/1") r := uniter.NewRelationer(s.apiRelUnit, s.dir, s.hooks) @@ -127,11 +149,6 @@ _, err = r.PrepareHook(hi) c.Assert(err, gc.IsNil) - // Verify PrepareHook created the dir. - fi, err := os.Stat(filepath.Join(s.dirPath, strconv.Itoa(s.rel.Id()))) - c.Assert(err, gc.IsNil) - c.Assert(fi, jc.Satisfies, os.FileInfo.IsDir) - err = r.CommitHook(hi) c.Assert(err, gc.IsNil) s.State.StartSync() @@ -411,11 +428,9 @@ r := uniter.NewRelationer(apiRelUnit, dir, hooks) c.Assert(r, jc.Satisfies, (*uniter.Relationer).IsImplicit) - // Join the relationer; the dir won't be created until necessary + // Join the relation. err = r.Join() c.Assert(err, gc.IsNil) - _, err = os.Stat(filepath.Join(relsDir, strconv.Itoa(rel.Id()))) - c.Assert(err, gc.NotNil) sub, err := logging.Unit("logging/0") c.Assert(err, gc.IsNil) err = sub.SetPrivateAddress("blah") @@ -435,11 +450,11 @@ c.Fatalf("unexpected hook generated") } - // Set it to Dying; check that the dir is removed. + // Set it to Dying; check that the dir is removed immediately. err = r.SetDying() c.Assert(err, gc.IsNil) - _, err = os.Stat(filepath.Join(relsDir, strconv.Itoa(rel.Id()))) - c.Assert(err, jc.Satisfies, os.IsNotExist) + path := strconv.Itoa(rel.Id()) + ft.Removed{path}.Check(c, relsDir) // Check that it left scope, by leaving scope on the other side and destroying // the relation. diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/worker/uniter/uniter.go juju-core-1.18.1/src/launchpad.net/juju-core/worker/uniter/uniter.go --- juju-core-1.18.0/src/launchpad.net/juju-core/worker/uniter/uniter.go 2014-04-04 16:53:35.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/worker/uniter/uniter.go 2014-04-11 09:53:38.000000000 +0000 @@ -29,6 +29,7 @@ "launchpad.net/juju-core/utils" "launchpad.net/juju-core/utils/exec" "launchpad.net/juju-core/utils/fslock" + "launchpad.net/juju-core/worker" "launchpad.net/juju-core/worker/uniter/charm" "launchpad.net/juju-core/worker/uniter/hook" "launchpad.net/juju-core/worker/uniter/jujuc" @@ -128,7 +129,7 @@ }() // Run modes until we encounter an error. - mode := ModeInit + mode := ModeContinue for err == nil { select { case <-u.tomb.Dying(): @@ -167,6 +168,13 @@ if err != nil { return err } + if u.unit.Life() == params.Dead { + // If we started up already dead, we should not progress further. If we + // become Dead immediately after starting up, we may well complete any + // operations in progress before detecting it; but that race is fundamental + // and inescapable, whereas this one is not. + return worker.ErrTerminateAgent + } if err = u.setupLocks(); err != nil { return err } @@ -191,16 +199,6 @@ u.uuid = env.UUID() u.envName = env.Name() - runListenerSocketPath := filepath.Join(u.baseDir, RunListenerFile) - logger.Debugf("starting juju-run listener on unix:%s", runListenerSocketPath) - u.runListener, err = NewRunListener(u, runListenerSocketPath) - if err != nil { - return err - } - // The socket needs to have permissions 777 in order for other users to use it. - if err := os.Chmod(runListenerSocketPath, 0777); err != nil { - return err - } u.relationers = map[int]*Relationer{} u.relationHooks = make(chan hook.Info) u.charm = charm.NewGitDir(filepath.Join(u.baseDir, "charm")) @@ -209,7 +207,20 @@ u.deployer = charm.NewGitDeployer(u.charm.Path(), deployerPath, bundles) u.sf = NewStateFile(filepath.Join(u.baseDir, "state", "uniter")) u.rand = rand.New(rand.NewSource(time.Now().Unix())) - return nil + + // If we start trying to listen for juju-run commands before we have valid + // relation state, surprising things will come to pass. + if err := u.restoreRelations(); err != nil { + return err + } + runListenerSocketPath := filepath.Join(u.baseDir, RunListenerFile) + logger.Debugf("starting juju-run listener on unix:%s", runListenerSocketPath) + u.runListener, err = NewRunListener(u, runListenerSocketPath) + if err != nil { + return err + } + // The socket needs to have permissions 777 in order for other users to use it. + return os.Chmod(runListenerSocketPath, 0777) } func (u *Uniter) Kill() { @@ -522,35 +533,69 @@ return hookName } -// restoreRelations reconciles the supplied relation state dirs with the +// getJoinedRelations finds out what relations the unit is *really* part of, +// working around the fact that pre-1.19 (1.18.1?) unit agents don't write a +// state dir for a relation until a remote unit joins. +func (u *Uniter) getJoinedRelations() (map[int]*uniter.Relation, error) { + var joinedRelationTags []string + for { + var err error + joinedRelationTags, err = u.unit.JoinedRelations() + if err == nil { + break + } + if params.IsCodeNotImplemented(err) { + logger.Infof("waiting for state server to be upgraded") + select { + case <-u.tomb.Dying(): + return nil, tomb.ErrDying + case <-time.After(15 * time.Second): + continue + } + } + return nil, err + } + joinedRelations := make(map[int]*uniter.Relation) + for _, tag := range joinedRelationTags { + relation, err := u.st.Relation(tag) + if err != nil { + return nil, err + } + joinedRelations[relation.Id()] = relation + } + return joinedRelations, nil +} + +// restoreRelations reconciles the local relation state dirs with the // remote state of the corresponding relations. func (u *Uniter) restoreRelations() error { - // TODO(dimitern): Get these from state, not from disk. - dirs, err := relation.ReadAllStateDirs(u.relationsDir) + joinedRelations, err := u.getJoinedRelations() if err != nil { return err } - for id, dir := range dirs { - remove := false - rel, err := u.st.RelationById(id) - if params.IsCodeNotFoundOrCodeUnauthorized(err) { - remove = true - } else if err != nil { + knownDirs, err := relation.ReadAllStateDirs(u.relationsDir) + if err != nil { + return err + } + for id, dir := range knownDirs { + if rel, ok := joinedRelations[id]; ok { + if err := u.addRelation(rel, dir); err != nil { + return err + } + } else if err := dir.Remove(); err != nil { return err } - err = u.addRelation(rel, dir) - if params.IsCodeCannotEnterScope(err) { - remove = true - } else if err != nil { + } + for id, rel := range joinedRelations { + if _, ok := knownDirs[id]; ok { + continue + } + dir, err := relation.ReadStateDir(u.relationsDir, id) + if err != nil { return err } - if remove { - // If the previous execution was interrupted in the process of - // joining or departing the relation, the directory will be empty - // and the state is sane. - if err := dir.Remove(); err != nil { - return fmt.Errorf("cannot synchronize relation state: %v", err) - } + if err := u.addRelation(rel, dir); err != nil { + return err } } return nil diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/worker/uniter/uniter_test.go juju-core-1.18.1/src/launchpad.net/juju-core/worker/uniter/uniter_test.go --- juju-core-1.18.0/src/launchpad.net/juju-core/worker/uniter/uniter_test.go 2014-04-04 16:57:31.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/worker/uniter/uniter_test.go 2014-04-11 09:55:10.000000000 +0000 @@ -32,6 +32,7 @@ "launchpad.net/juju-core/state/api/params" apiuniter "launchpad.net/juju-core/state/api/uniter" coretesting "launchpad.net/juju-core/testing" + ft "launchpad.net/juju-core/testing/filetesting" "launchpad.net/juju-core/utils" utilexec "launchpad.net/juju-core/utils/exec" "launchpad.net/juju-core/utils/fslock" @@ -966,9 +967,58 @@ waitHooks{}, // TODO BUG(?): the unit doesn't leave the scope, leaving the relation // unkillable without direct intervention. I'm pretty sure it's not a - // uniter bug -- it should be the responisbility of `juju remove-unit + // uniter bug -- it should be the responsibility of `juju remove-unit // --force` to cause the unit to leave any relation scopes it may be // in -- but it's worth noting here all the same. + ), ut( + "unknown local relation dir is removed", + quickStartRelation{}, + stopUniter{}, + custom{func(c *gc.C, ctx *context) { + ft.Dir{"state/relations/90210", 0755}.Create(c, ctx.path) + }}, + startUniter{}, + waitHooks{"config-changed"}, + custom{func(c *gc.C, ctx *context) { + ft.Removed{"state/relations/90210"}.Check(c, ctx.path) + }}, + ), ut( + "all relations are available to config-changed on bounce, even if state dir is missing", + createCharm{ + customize: func(c *gc.C, ctx *context, path string) { + script := "relation-ids db > relations.out && chmod 644 relations.out" + appendHook(c, path, "config-changed", script) + }, + }, + serveCharm{}, + createUniter{}, + waitUnit{ + status: params.StatusStarted, + }, + waitHooks{"install", "config-changed", "start"}, + addRelation{waitJoin: true}, + stopUniter{}, + custom{func(c *gc.C, ctx *context) { + // Check the state dir was created, and remove it. + path := fmt.Sprintf("state/relations/%d", ctx.relation.Id()) + ft.Dir{path, 0755}.Check(c, ctx.path) + ft.Removed{path}.Create(c, ctx.path) + + // Check that config-changed didn't record any relations, because + // they shouldn't been available until after the start hook. + ft.File{"charm/relations.out", "", 0644}.Check(c, ctx.path) + }}, + startUniter{}, + waitHooks{"config-changed"}, + custom{func(c *gc.C, ctx *context) { + // Check the state dir was recreated. + path := fmt.Sprintf("state/relations/%d", ctx.relation.Id()) + ft.Dir{path, 0755}.Check(c, ctx.path) + + // Check that config-changed did record the joined relations. + data := fmt.Sprintf("db:%d\n", ctx.relation.Id()) + ft.File{"charm/relations.out", data, 0644}.Check(c, ctx.path) + }}, ), } @@ -1671,7 +1721,7 @@ } type addRelation struct { - testing.JujuConnSuite + waitJoin bool } func (s addRelation) step(c *gc.C, ctx *context) { @@ -1686,6 +1736,27 @@ ctx.relation, err = ctx.st.AddRelation(eps...) c.Assert(err, gc.IsNil) ctx.relationUnits = map[string]*state.RelationUnit{} + if !s.waitJoin { + return + } + + // It's hard to do this properly (watching scope) without perturbing other tests. + ru, err := ctx.relation.Unit(ctx.unit) + c.Assert(err, gc.IsNil) + timeout := time.After(worstCase) + for { + c.Logf("waiting to join relation") + select { + case <-timeout: + c.Fatalf("failed to join relation") + case <-time.After(coretesting.ShortWait): + inScope, err := ru.InScope() + c.Assert(err, gc.IsNil) + if inScope { + return + } + } + } } type addRelationUnit struct{} diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/worker/upgrader/export_test.go juju-core-1.18.1/src/launchpad.net/juju-core/worker/upgrader/export_test.go --- juju-core-1.18.0/src/launchpad.net/juju-core/worker/upgrader/export_test.go 2014-04-04 16:53:35.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/worker/upgrader/export_test.go 2014-04-11 09:53:38.000000000 +0000 @@ -8,7 +8,10 @@ "launchpad.net/juju-core/utils" ) -var RetryAfter = &retryAfter +var ( + RetryAfter = &retryAfter + AllowedTargetVersion = allowedTargetVersion +) func EnsureTools(u *Upgrader, agentTools *tools.Tools, hostnameVerification utils.SSLHostnameVerification) error { return u.ensureTools(agentTools, hostnameVerification) diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/worker/upgrader/upgrader.go juju-core-1.18.1/src/launchpad.net/juju-core/worker/upgrader/upgrader.go --- juju-core-1.18.0/src/launchpad.net/juju-core/worker/upgrader/upgrader.go 2014-04-04 16:53:35.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/worker/upgrader/upgrader.go 2014-04-11 09:53:38.000000000 +0000 @@ -73,6 +73,18 @@ return u.Wait() } +// allowedTargetVersion checks if targetVersion is too different from +// curVersion to allow a downgrade. +func allowedTargetVersion(curVersion, targetVersion version.Number) bool { + if targetVersion.Major < curVersion.Major { + return false + } + if targetVersion.Major == curVersion.Major && targetVersion.Minor < curVersion.Minor { + return false + } + return true +} + func (u *Upgrader) loop() error { currentTools := &coretools.Tools{Version: version.Current} err := u.st.SetVersion(u.tag, currentTools.Version) @@ -112,33 +124,43 @@ case <-dying: return nil } - if wantVersion != currentTools.Version.Number { - logger.Infof("upgrade requested from %v to %v", currentTools.Version, wantVersion) - // TODO(dimitern) 2013-10-03 bug #1234715 - // Add a testing HTTPS storage to verify the - // disableSSLHostnameVerification behavior here. - wantTools, hostnameVerification, err = u.st.Tools(u.tag) - if err != nil { - // Not being able to lookup Tools is considered fatal - return err - } - // The worker cannot be stopped while we're downloading - // the tools - this means that even if the API is going down - // repeatedly (causing the agent to be stopped), as long - // as we have got as far as this, we will still be able to - // upgrade the agent. - err := u.ensureTools(wantTools, hostnameVerification) - if err == nil { - return &UpgradeReadyError{ - OldTools: version.Current, - NewTools: wantTools.Version, - AgentName: u.tag, - DataDir: u.dataDir, - } + if wantVersion == currentTools.Version.Number { + continue + } else if !allowedTargetVersion(version.Current.Number, wantVersion) { + // See also bug #1299802 where when upgrading from + // 1.16 to 1.18 there is a race condition that can + // cause the unit agent to upgrade, and then want to + // downgrade when its associate machine agent has not + // finished upgrading. + logger.Infof("desired tool version: %s is older than current %s, refusing to downgrade", + wantVersion, version.Current) + continue + } + logger.Infof("upgrade requested from %v to %v", currentTools.Version, wantVersion) + // TODO(dimitern) 2013-10-03 bug #1234715 + // Add a testing HTTPS storage to verify the + // disableSSLHostnameVerification behavior here. + wantTools, hostnameVerification, err = u.st.Tools(u.tag) + if err != nil { + // Not being able to lookup Tools is considered fatal + return err + } + // The worker cannot be stopped while we're downloading + // the tools - this means that even if the API is going down + // repeatedly (causing the agent to be stopped), as long + // as we have got as far as this, we will still be able to + // upgrade the agent. + err := u.ensureTools(wantTools, hostnameVerification) + if err == nil { + return &UpgradeReadyError{ + OldTools: version.Current, + NewTools: wantTools.Version, + AgentName: u.tag, + DataDir: u.dataDir, } - logger.Errorf("failed to fetch tools from %q: %v", wantTools.URL, err) - retry = retryAfter() } + logger.Errorf("failed to fetch tools from %q: %v", wantTools.URL, err) + retry = retryAfter() } } diff -Nru juju-core-1.18.0/src/launchpad.net/juju-core/worker/upgrader/upgrader_test.go juju-core-1.18.1/src/launchpad.net/juju-core/worker/upgrader/upgrader_test.go --- juju-core-1.18.0/src/launchpad.net/juju-core/worker/upgrader/upgrader_test.go 2014-04-04 16:53:35.000000000 +0000 +++ juju-core-1.18.1/src/launchpad.net/juju-core/worker/upgrader/upgrader_test.go 2014-04-11 09:55:10.000000000 +0000 @@ -42,7 +42,10 @@ oldRetryAfter func() <-chan time.Time } +type AllowedTargetVersionSuite struct{} + var _ = gc.Suite(&UpgraderSuite{}) +var _ = gc.Suite(&AllowedTargetVersionSuite{}) func (s *UpgraderSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) @@ -227,7 +230,75 @@ // something invalid and ensure we don't actually get an error, because // it doesn't actually do an HTTP request u := s.makeUpgrader() - newTools.URL = "http://localhost:999999/invalid/path/tools.tgz" + newTools.URL = "http://0.1.2.3/invalid/path/tools.tgz" err := upgrader.EnsureTools(u, newTools, utils.VerifySSLHostnames) c.Assert(err, gc.IsNil) } + +func (s *UpgraderSuite) TestUpgraderRefusesToDowngradeMinorVersions(c *gc.C) { + stor := s.Conn.Environ.Storage() + origTools := envtesting.PrimeTools(c, stor, s.DataDir(), version.MustParseBinary("5.4.3-precise-amd64")) + s.PatchValue(&version.Current, origTools.Version) + downgradeTools := envtesting.AssertUploadFakeToolsVersions( + c, stor, version.MustParseBinary("5.3.3-precise-amd64"))[0] + err := statetesting.SetAgentVersion(s.State, downgradeTools.Version.Number) + c.Assert(err, gc.IsNil) + + u := s.makeUpgrader() + err = u.Stop() + // If the upgrade would have triggered, we would have gotten an + // UpgradeReadyError, since it was skipped, we get no error + c.Check(err, gc.IsNil) + _, err = agenttools.ReadTools(s.DataDir(), downgradeTools.Version) + // TODO: ReadTools *should* be returning some form of NotFoundError, + // however, it just passes back a fmt.Errorf so we live with it + // c.Assert(err, jc.Satisfies, errors.IsNotFoundError) + c.Check(err, gc.ErrorMatches, "cannot read tools metadata in tools directory.*no such file or directory") +} + +func (s *UpgraderSuite) TestUpgraderAllowsDowngradingPatchVersions(c *gc.C) { + stor := s.Conn.Environ.Storage() + origTools := envtesting.PrimeTools(c, stor, s.DataDir(), version.MustParseBinary("5.4.3-precise-amd64")) + s.PatchValue(&version.Current, origTools.Version) + downgradeTools := envtesting.AssertUploadFakeToolsVersions( + c, stor, version.MustParseBinary("5.4.2-precise-amd64"))[0] + err := statetesting.SetAgentVersion(s.State, downgradeTools.Version.Number) + c.Assert(err, gc.IsNil) + + dummy.SetStorageDelay(coretesting.ShortWait) + + u := s.makeUpgrader() + err = u.Stop() + envtesting.CheckUpgraderReadyError(c, err, &upgrader.UpgradeReadyError{ + AgentName: s.machine.Tag(), + OldTools: origTools.Version, + NewTools: downgradeTools.Version, + DataDir: s.DataDir(), + }) + foundTools, err := agenttools.ReadTools(s.DataDir(), downgradeTools.Version) + c.Assert(err, gc.IsNil) + envtesting.CheckTools(c, foundTools, downgradeTools) +} + +type allowedTest struct { + current string + target string + allowed bool +} + +func (s *AllowedTargetVersionSuite) TestAllowedTargetVersionSuite(c *gc.C) { + cases := []allowedTest{ + {current: "1.2.3", target: "1.3.3", allowed: true}, + {current: "1.2.3", target: "1.2.3", allowed: true}, + {current: "1.2.3", target: "2.2.3", allowed: true}, + {current: "1.2.3", target: "1.1.3", allowed: false}, + {current: "1.2.3", target: "1.2.2", allowed: true}, + {current: "1.2.3", target: "0.2.3", allowed: false}, + } + for i, test := range cases { + c.Logf("test case %d, %#v", i, test) + current := version.MustParse(test.current) + target := version.MustParse(test.target) + c.Check(upgrader.AllowedTargetVersion(current, target), gc.Equals, test.allowed) + } +}